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