1 /*
2 * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include "native_client/src/trusted/service_runtime/sys_fdio.h"
8
9 #include <string.h>
10
11 #include "native_client/src/trusted/desc/nacl_desc_base.h"
12 #include "native_client/src/trusted/desc/nacl_desc_io.h"
13 #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
14 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
15 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
16 #include "native_client/src/trusted/service_runtime/nacl_copy.h"
17 #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
18 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
19
20
21 static size_t const kdefault_io_buffer_bytes_to_log = 64;
22
NaClSysDup(struct NaClAppThread * natp,int oldfd)23 int32_t NaClSysDup(struct NaClAppThread *natp,
24 int oldfd) {
25 struct NaClApp *nap = natp->nap;
26 int retval;
27 struct NaClDesc *old_nd;
28
29 NaClLog(3, "NaClSysDup(0x%08"NACL_PRIxPTR", %d)\n",
30 (uintptr_t) natp, oldfd);
31 old_nd = NaClAppGetDesc(nap, oldfd);
32 if (NULL == old_nd) {
33 retval = -NACL_ABI_EBADF;
34 goto done;
35 }
36 retval = NaClAppSetDescAvail(nap, old_nd);
37 done:
38 return retval;
39 }
40
NaClSysDup2(struct NaClAppThread * natp,int oldfd,int newfd)41 int32_t NaClSysDup2(struct NaClAppThread *natp,
42 int oldfd,
43 int newfd) {
44 struct NaClApp *nap = natp->nap;
45 int retval;
46 struct NaClDesc *old_nd;
47
48 NaClLog(3, "NaClSysDup(0x%08"NACL_PRIxPTR", %d, %d)\n",
49 (uintptr_t) natp, oldfd, newfd);
50 if (newfd < 0) {
51 retval = -NACL_ABI_EINVAL;
52 goto done;
53 }
54 /*
55 * TODO(bsy): is this a reasonable largest sane value? The
56 * descriptor array shouldn't get too large.
57 */
58 if (newfd >= NACL_MAX_FD) {
59 retval = -NACL_ABI_EINVAL;
60 goto done;
61 }
62 old_nd = NaClAppGetDesc(nap, oldfd);
63 if (NULL == old_nd) {
64 retval = -NACL_ABI_EBADF;
65 goto done;
66 }
67 NaClAppSetDesc(nap, newfd, old_nd);
68 retval = newfd;
69 done:
70 return retval;
71 }
72
NaClSysClose(struct NaClAppThread * natp,int d)73 int32_t NaClSysClose(struct NaClAppThread *natp,
74 int d) {
75 struct NaClApp *nap = natp->nap;
76 int retval = -NACL_ABI_EBADF;
77 struct NaClDesc *ndp;
78
79 NaClLog(3, "Entered NaClSysClose(0x%08"NACL_PRIxPTR", %d)\n",
80 (uintptr_t) natp, d);
81
82 NaClFastMutexLock(&nap->desc_mu);
83 ndp = NaClAppGetDescMu(nap, d);
84 if (NULL != ndp) {
85 NaClAppSetDescMu(nap, d, NULL); /* Unref the desc_tbl */
86 }
87 NaClFastMutexUnlock(&nap->desc_mu);
88 NaClLog(5, "Invoking Close virtual function of object 0x%08"NACL_PRIxPTR"\n",
89 (uintptr_t) ndp);
90 if (NULL != ndp) {
91 NaClDescUnref(ndp);
92 retval = 0;
93 }
94
95 return retval;
96 }
97
NaClSysIsatty(struct NaClAppThread * natp,int d)98 int32_t NaClSysIsatty(struct NaClAppThread *natp,
99 int d) {
100 struct NaClApp *nap = natp->nap;
101 int retval = -NACL_ABI_EBADF;
102 struct NaClDesc *ndp;
103
104 NaClLog(3, "Entered NaClSysIsatty(0x%08"NACL_PRIxPTR", %d)\n",
105 (uintptr_t) natp, d);
106
107 if (!NaClAclBypassChecks) {
108 return -NACL_ABI_EACCES;
109 }
110
111 ndp = NaClAppGetDesc(nap, d);
112 if (NULL == ndp) {
113 NaClLog(4, "bad desc\n");
114 return -NACL_ABI_EBADF;
115 }
116
117 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->Isatty)(ndp);
118 NaClDescUnref(ndp);
119 return retval;
120 }
121
NaClSysGetdents(struct NaClAppThread * natp,int d,uint32_t dirp,size_t count)122 int32_t NaClSysGetdents(struct NaClAppThread *natp,
123 int d,
124 uint32_t dirp,
125 size_t count) {
126 struct NaClApp *nap = natp->nap;
127 int32_t retval = -NACL_ABI_EINVAL;
128 ssize_t getdents_ret;
129 uintptr_t sysaddr;
130 struct NaClDesc *ndp;
131
132 NaClLog(3,
133 ("Entered NaClSysGetdents(0x%08"NACL_PRIxPTR", "
134 "%d, 0x%08"NACL_PRIx32", "
135 "%"NACL_PRIuS"[0x%"NACL_PRIxS"])\n"),
136 (uintptr_t) natp, d, dirp, count, count);
137
138 if (!NaClFileAccessEnabled()) {
139 /*
140 * Filesystem access is disabled, so disable the getdents() syscall.
141 * We do this for security hardening, though it should be redundant,
142 * because untrusted code should not be able to open any directory
143 * descriptors (i.e. descriptors with a non-trivial Getdents()
144 * implementation).
145 */
146 return -NACL_ABI_EACCES;
147 }
148
149 ndp = NaClAppGetDesc(nap, d);
150 if (NULL == ndp) {
151 retval = -NACL_ABI_EBADF;
152 goto cleanup;
153 }
154
155 /*
156 * Generic NaClCopyOutToUser is not sufficient, since buffer size
157 * |count| is arbitrary and we wouldn't want to have to allocate
158 * memory in trusted address space to match.
159 */
160 sysaddr = NaClUserToSysAddrRange(nap, dirp, count);
161 if (kNaClBadAddress == sysaddr) {
162 NaClLog(4, " illegal address for directory data\n");
163 retval = -NACL_ABI_EFAULT;
164 goto cleanup_unref;
165 }
166
167 /*
168 * Clamp count to INT32_MAX to avoid the possibility of Getdents returning
169 * a value that is outside the range of an int32.
170 */
171 if (count > INT32_MAX) {
172 count = INT32_MAX;
173 }
174 /*
175 * Grab addr space lock; getdents should not normally block, though
176 * if the directory is on a networked filesystem this could, and
177 * cause mmap to be slower on Windows.
178 */
179 NaClXMutexLock(&nap->mu);
180 getdents_ret = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
181 Getdents)(ndp,
182 (void *) sysaddr,
183 count);
184 NaClXMutexUnlock(&nap->mu);
185 /* drop addr space lock */
186 if ((getdents_ret < INT32_MIN && !NaClSSizeIsNegErrno(&getdents_ret))
187 || INT32_MAX < getdents_ret) {
188 /* This should never happen, because we already clamped the input count */
189 NaClLog(LOG_FATAL, "Overflow in Getdents: return value is %"NACL_PRIxS,
190 (size_t) getdents_ret);
191 } else {
192 retval = (int32_t) getdents_ret;
193 }
194 if (retval > 0) {
195 NaClLog(4, "getdents returned %d bytes\n", retval);
196 NaClLog(8, "getdents result: %.*s\n", retval, (char *) sysaddr);
197 } else {
198 NaClLog(4, "getdents returned %d\n", retval);
199 }
200
201 cleanup_unref:
202 NaClDescUnref(ndp);
203
204 cleanup:
205 return retval;
206 }
207
NaClSysRead(struct NaClAppThread * natp,int d,uint32_t buf,uint32_t count)208 int32_t NaClSysRead(struct NaClAppThread *natp,
209 int d,
210 uint32_t buf,
211 uint32_t count) {
212 struct NaClApp *nap = natp->nap;
213 int32_t retval = -NACL_ABI_EINVAL;
214 ssize_t read_result = -NACL_ABI_EINVAL;
215 uintptr_t sysaddr;
216 struct NaClDesc *ndp;
217 size_t log_bytes;
218 char const *ellipsis = "";
219
220 NaClLog(3,
221 ("Entered NaClSysRead(0x%08"NACL_PRIxPTR", "
222 "%d, 0x%08"NACL_PRIx32", "
223 "%"NACL_PRIu32"[0x%"NACL_PRIx32"])\n"),
224 (uintptr_t) natp, d, buf, count, count);
225
226 ndp = NaClAppGetDesc(nap, d);
227 if (NULL == ndp) {
228 retval = -NACL_ABI_EBADF;
229 goto cleanup;
230 }
231
232 sysaddr = NaClUserToSysAddrRange(nap, buf, count);
233 if (kNaClBadAddress == sysaddr) {
234 NaClDescUnref(ndp);
235 retval = -NACL_ABI_EFAULT;
236 goto cleanup;
237 }
238
239 /*
240 * The maximum length for read and write is INT32_MAX--anything larger and
241 * the return value would overflow. Passing larger values isn't an error--
242 * we'll just clamp the request size if it's too large.
243 */
244 if (count > INT32_MAX) {
245 count = INT32_MAX;
246 }
247
248 NaClVmIoWillStart(nap, buf, buf + count - 1);
249 read_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
250 Read)(ndp, (void *) sysaddr, count);
251 NaClVmIoHasEnded(nap, buf, buf + count - 1);
252 if (read_result > 0) {
253 NaClLog(4, "read returned %"NACL_PRIdS" bytes\n", read_result);
254 log_bytes = (size_t) read_result;
255 if (log_bytes > INT32_MAX) {
256 log_bytes = INT32_MAX;
257 ellipsis = "...";
258 }
259 if (NaClLogGetVerbosity() < 10) {
260 if (log_bytes > kdefault_io_buffer_bytes_to_log) {
261 log_bytes = kdefault_io_buffer_bytes_to_log;
262 ellipsis = "...";
263 }
264 }
265 NaClLog(8, "read result: %.*s%s\n",
266 (int) log_bytes, (char *) sysaddr, ellipsis);
267 } else {
268 NaClLog(4, "read returned %"NACL_PRIdS"\n", read_result);
269 }
270 NaClDescUnref(ndp);
271
272 /* This cast is safe because we clamped count above.*/
273 retval = (int32_t) read_result;
274 cleanup:
275 return retval;
276 }
277
NaClSysWrite(struct NaClAppThread * natp,int d,uint32_t buf,uint32_t count)278 int32_t NaClSysWrite(struct NaClAppThread *natp,
279 int d,
280 uint32_t buf,
281 uint32_t count) {
282 struct NaClApp *nap = natp->nap;
283 int32_t retval = -NACL_ABI_EINVAL;
284 ssize_t write_result = -NACL_ABI_EINVAL;
285 uintptr_t sysaddr;
286 char const *ellipsis = "";
287 struct NaClDesc *ndp;
288 size_t log_bytes;
289
290 NaClLog(3,
291 "Entered NaClSysWrite(0x%08"NACL_PRIxPTR", "
292 "%d, 0x%08"NACL_PRIx32", "
293 "%"NACL_PRIu32"[0x%"NACL_PRIx32"])\n",
294 (uintptr_t) natp, d, buf, count, count);
295
296 ndp = NaClAppGetDesc(nap, d);
297 NaClLog(4, " ndp = %"NACL_PRIxPTR"\n", (uintptr_t) ndp);
298 if (NULL == ndp) {
299 retval = -NACL_ABI_EBADF;
300 goto cleanup;
301 }
302
303 sysaddr = NaClUserToSysAddrRange(nap, buf, count);
304 if (kNaClBadAddress == sysaddr) {
305 NaClDescUnref(ndp);
306 retval = -NACL_ABI_EFAULT;
307 goto cleanup;
308 }
309
310 log_bytes = count;
311 if (log_bytes > INT32_MAX) {
312 log_bytes = INT32_MAX;
313 ellipsis = "...";
314 }
315 if (NaClLogGetVerbosity() < 10) {
316 if (log_bytes > kdefault_io_buffer_bytes_to_log) {
317 log_bytes = kdefault_io_buffer_bytes_to_log;
318 ellipsis = "...";
319 }
320 }
321 NaClLog(8, "In NaClSysWrite(%d, %.*s%s, %"NACL_PRIu32")\n",
322 d, (int) log_bytes, (char *) sysaddr, ellipsis, count);
323
324 /*
325 * The maximum length for read and write is INT32_MAX--anything larger and
326 * the return value would overflow. Passing larger values isn't an error--
327 * we'll just clamp the request size if it's too large.
328 */
329 if (count > INT32_MAX) {
330 count = INT32_MAX;
331 }
332
333 NaClVmIoWillStart(nap, buf, buf + count - 1);
334 write_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
335 Write)(ndp, (void *) sysaddr, count);
336 NaClVmIoHasEnded(nap, buf, buf + count - 1);
337
338 NaClDescUnref(ndp);
339
340 /* This cast is safe because we clamped count above.*/
341 retval = (int32_t) write_result;
342
343 cleanup:
344 return retval;
345 }
346
347 /*
348 * This implements 64-bit offsets, so we use |offp| as an in/out
349 * address so we can have a 64 bit return value.
350 */
NaClSysLseek(struct NaClAppThread * natp,int d,uint32_t offp,int whence)351 int32_t NaClSysLseek(struct NaClAppThread *natp,
352 int d,
353 uint32_t offp,
354 int whence) {
355 struct NaClApp *nap = natp->nap;
356 nacl_abi_off_t offset;
357 nacl_off64_t retval64;
358 int32_t retval = -NACL_ABI_EINVAL;
359 struct NaClDesc *ndp;
360
361 NaClLog(3,
362 ("Entered NaClSysLseek(0x%08"NACL_PRIxPTR", %d,"
363 " 0x%08"NACL_PRIx32", %d)\n"),
364 (uintptr_t) natp, d, offp, whence);
365
366 ndp = NaClAppGetDesc(nap, d);
367 if (NULL == ndp) {
368 retval = -NACL_ABI_EBADF;
369 goto cleanup;
370 }
371
372 if (!NaClCopyInFromUser(nap, &offset, offp, sizeof offset)) {
373 retval = -NACL_ABI_EFAULT;
374 goto cleanup_unref;
375 }
376 NaClLog(4, "offset 0x%08"NACL_PRIx64"\n", (uint64_t) offset);
377
378 retval64 = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
379 Seek)(ndp, (nacl_off64_t) offset, whence);
380 if (NaClOff64IsNegErrno(&retval64)) {
381 retval = (int32_t) retval64;
382 } else {
383 if (NaClCopyOutToUser(nap, offp, &retval64, sizeof retval64)) {
384 retval = 0;
385 } else {
386 NaClLog(LOG_FATAL,
387 "NaClSysLseek: in/out ptr became invalid at copyout?\n");
388 }
389 }
390 cleanup_unref:
391 NaClDescUnref(ndp);
392 cleanup:
393 return retval;
394 }
395
NaClSysFstat(struct NaClAppThread * natp,int d,uint32_t nasp)396 int32_t NaClSysFstat(struct NaClAppThread *natp,
397 int d,
398 uint32_t nasp) {
399 struct NaClApp *nap = natp->nap;
400 int32_t retval = -NACL_ABI_EINVAL;
401 struct NaClDesc *ndp;
402 struct nacl_abi_stat result;
403
404 NaClLog(3,
405 ("Entered NaClSysFstat(0x%08"NACL_PRIxPTR
406 ", %d, 0x%08"NACL_PRIx32")\n"),
407 (uintptr_t) natp, d, nasp);
408
409 ndp = NaClAppGetDesc(nap, d);
410 if (NULL == ndp) {
411 NaClLog(4, "bad desc\n");
412 retval = -NACL_ABI_EBADF;
413 goto cleanup;
414 }
415
416 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
417 Fstat)(ndp, &result);
418 if (0 == retval) {
419 if (!NaClFileAccessEnabled()) {
420 result.nacl_abi_st_ino = NACL_FAKE_INODE_NUM;
421 }
422 if (!NaClCopyOutToUser(nap, nasp, &result, sizeof result)) {
423 retval = -NACL_ABI_EFAULT;
424 }
425 }
426
427 NaClDescUnref(ndp);
428 cleanup:
429 return retval;
430 }
431
NaClSysFchdir(struct NaClAppThread * natp,int d)432 int32_t NaClSysFchdir(struct NaClAppThread *natp,
433 int d) {
434 struct NaClApp *nap = natp->nap;
435 struct NaClDesc *ndp;
436 int32_t retval = -NACL_ABI_EINVAL;
437
438 NaClLog(3,
439 ("Entered NaClSysFchdir(0x%08"NACL_PRIxPTR", %d)\n"),
440 (uintptr_t) natp, d);
441
442 ndp = NaClAppGetDesc(nap, d);
443 if (NULL == ndp) {
444 retval = -NACL_ABI_EBADF;
445 goto cleanup;
446 }
447
448 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
449 Fchdir)(ndp);
450
451 NaClDescUnref(ndp);
452 cleanup:
453 return retval;
454 }
455
NaClSysFchmod(struct NaClAppThread * natp,int d,int mode)456 int32_t NaClSysFchmod(struct NaClAppThread *natp,
457 int d,
458 int mode) {
459 struct NaClApp *nap = natp->nap;
460 struct NaClDesc *ndp;
461 int32_t retval = -NACL_ABI_EINVAL;
462
463 NaClLog(3,
464 ("Entered NaClSysFchmod(0x%08"NACL_PRIxPTR", %d, 0x%x)\n"),
465 (uintptr_t) natp, d, mode);
466
467 ndp = NaClAppGetDesc(nap, d);
468 if (NULL == ndp) {
469 retval = -NACL_ABI_EBADF;
470 goto cleanup;
471 }
472
473 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
474 Fchmod)(ndp, mode);
475
476 NaClDescUnref(ndp);
477 cleanup:
478 return retval;
479 }
480
NaClSysFsync(struct NaClAppThread * natp,int d)481 int32_t NaClSysFsync(struct NaClAppThread *natp,
482 int d) {
483 struct NaClApp *nap = natp->nap;
484 struct NaClDesc *ndp;
485 int32_t retval = -NACL_ABI_EINVAL;
486
487 NaClLog(3,
488 ("Entered NaClSysFsync(0x%08"NACL_PRIxPTR", %d)\n"),
489 (uintptr_t) natp, d);
490
491 ndp = NaClAppGetDesc(nap, d);
492 if (NULL == ndp) {
493 retval = -NACL_ABI_EBADF;
494 goto cleanup;
495 }
496
497 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
498 Fsync)(ndp);
499
500 NaClDescUnref(ndp);
501 cleanup:
502 return retval;
503 }
504
NaClSysFdatasync(struct NaClAppThread * natp,int d)505 int32_t NaClSysFdatasync(struct NaClAppThread *natp,
506 int d) {
507 struct NaClApp *nap = natp->nap;
508 struct NaClDesc *ndp;
509 int32_t retval = -NACL_ABI_EINVAL;
510
511 NaClLog(3,
512 ("Entered NaClSysFdatasync(0x%08"NACL_PRIxPTR", %d)\n"),
513 (uintptr_t) natp, d);
514
515 ndp = NaClAppGetDesc(nap, d);
516 if (NULL == ndp) {
517 retval = -NACL_ABI_EBADF;
518 goto cleanup;
519 }
520
521 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
522 Fdatasync)(ndp);
523
524 NaClDescUnref(ndp);
525 cleanup:
526 return retval;
527 }
528
NaClSysFtruncate(struct NaClAppThread * natp,int d,uint32_t lengthp)529 int32_t NaClSysFtruncate(struct NaClAppThread *natp,
530 int d,
531 uint32_t lengthp) {
532 struct NaClApp *nap = natp->nap;
533 struct NaClDesc *ndp;
534 nacl_abi_off_t length;
535 int32_t retval = -NACL_ABI_EINVAL;
536
537 NaClLog(3,
538 ("Entered NaClSysFtruncate(0x%08"NACL_PRIxPTR", %d,"
539 " 0x%"NACL_PRIx32")\n"),
540 (uintptr_t) natp, d, lengthp);
541
542 ndp = NaClAppGetDesc(nap, d);
543 if (NULL == ndp) {
544 retval = -NACL_ABI_EBADF;
545 goto cleanup;
546 }
547 if (!NaClCopyInFromUser(nap, &length, lengthp, sizeof length)) {
548 retval = -NACL_ABI_EFAULT;
549 goto cleanup_unref;
550 }
551 NaClLog(4, "length 0x%08"NACL_PRIx64"\n", (uint64_t) length);
552
553
554 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
555 Ftruncate)(ndp, length);
556
557 cleanup_unref:
558 NaClDescUnref(ndp);
559 cleanup:
560 return retval;
561 }
562