73941c88f3227ae8aedcf5fb62b7fc3c43529c99
[platform/framework/web/crosswalk.git] / src / native_client / src / trusted / gio / gio_shm.c
1 /*
2  * Copyright (c) 2012 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 <errno.h>
8 #include <string.h>
9
10 #include "native_client/src/include/portability.h"
11
12 #include "native_client/src/shared/platform/nacl_check.h"
13 #include "native_client/src/shared/platform/nacl_host_desc.h"
14 #include "native_client/src/shared/platform/nacl_log.h"
15
16 #include "native_client/src/trusted/gio/gio_shm.h"
17 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
18 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
19 #include "native_client/src/trusted/service_runtime/nacl_config.h"
20 #include "native_client/src/trusted/service_runtime/sel_util.h"
21
22 #include "native_client/src/trusted/desc/nacl_desc_base.h"
23 #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
24 #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
25
26 /*
27  * This code maps in GIO_SHM_WINDOWSIZE bytes at a time for doing
28  * "I/O" from/to the shared memory object.  This value must be an
29  * integer multiple of NACL_MAP_PAGESIZE.
30  */
31 #define GIO_SHM_WINDOWSIZE  (16 * NACL_MAP_PAGESIZE)
32
33 /*
34  * Release current window if it exists, then map in window at the
35  * provided new_window_offset.  This is akin to filbuf.
36  *
37  * Preconditions: 0 == (new_win_offset & (NACL_MAP_PAGESIZE - 1))
38  *                new_win_offset < self->shm_sz
39  */
40 static int NaClGioShmSetWindow(struct NaClGioShm  *self,
41                                size_t             new_win_offset) {
42   uintptr_t map_result;
43   size_t    actual_len;
44
45   NaClLog(4,
46           "NaClGioShmSetWindow: new_win_offset 0x%"NACL_PRIxS"\n",
47           new_win_offset);
48   if (0 != (new_win_offset & (NACL_MAP_PAGESIZE - 1))) {
49     NaClLog(LOG_FATAL,
50             ("NaClGioShmSetWindow: internal error, requested"
51              " new window offset 0x%"NACL_PRIxS" is not aligned.\n"),
52             new_win_offset);
53   }
54
55   if (new_win_offset >= self->shm_sz) {
56     NaClLog(LOG_FATAL,
57             ("NaClGioShmSetWindow: setting window beyond end of shm object"
58              " offset 0x%"NACL_PRIxS", size 0x%"NACL_PRIxS"\n"),
59             new_win_offset, self->shm_sz);
60   }
61
62   if (NULL != self->cur_window) {
63     NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window,
64                         self->window_size);
65   }
66   self->cur_window = NULL;
67   self->window_size = 0;
68
69   /*
70    * The Map virtual function will NOT pad space beyond the end of the
71    * memory mapping object with zero-filled pages.  This is done for
72    * user code in nacl_syscall_common.c (NaClSysMmap), and the Map
73    * virtual function exposes the behavioral inconsistencies wrt
74    * allowing but ignoring mapping an offset beyond the end of file
75    * (linux) versus disallowing the mapping (MapViewOfFileEx).
76    *
77    * Here, we know the actual size of the shm object, and can deal
78    * with it.
79    */
80   actual_len = GIO_SHM_WINDOWSIZE;
81   if (actual_len > self->shm_sz - new_win_offset) {
82     actual_len = self->shm_sz - new_win_offset;
83   }
84   map_result =
85       (*((struct NaClDescVtbl const *) self->shmp->base.vtbl)->
86        Map)(self->shmp,
87             NaClDescEffectorTrustedMem(),
88             (void *) NULL,
89             actual_len,
90             NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
91             NACL_ABI_MAP_SHARED,
92             (nacl_off64_t) new_win_offset);
93   NaClLog(4,
94           "NaClGioShmSetWindow: Map returned 0x%"NACL_PRIxPTR"\n",
95           map_result);
96   if (NaClPtrIsNegErrno(&map_result)) {
97     return 0;
98   }
99
100   self->cur_window = (char *) map_result;
101   self->window_size = actual_len;
102   self->window_offset = new_win_offset;
103
104   return 1;
105 }
106
107 static ssize_t NaClGioShmReadOrWrite(struct Gio *vself,
108                                      void       *buf,
109                                      size_t     count,
110                                      int        is_write) {
111   struct NaClGioShm *self = (struct NaClGioShm *) vself;
112   size_t            new_window_offset;
113   size_t            transfer;
114   size_t            window_end;
115   size_t            window_remain;
116   size_t            sofar;
117
118   NaClLog(4,
119           ("NaClGioShmReadOrWrite: 0x%"NACL_PRIxPTR","
120            " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS", %d\n"),
121           (uintptr_t) vself,
122           (uintptr_t) buf,
123           count,
124           is_write);
125   sofar = 0;
126   while (count > 0) {
127     NaClLog(4, "NaClGioShmReadOrWrite: count 0x%"NACL_PRIxS"\n", count);
128     if (self->io_offset >= self->shm_sz) {
129       break;
130     }
131     NaClLog(4, "   cur_window 0x%"NACL_PRIxPTR"\n",
132             (uintptr_t) self->cur_window);
133     NaClLog(4, "    io_offset 0x%"NACL_PRIxS"\n", self->io_offset);
134     NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset);
135     if (NULL == self->cur_window
136         || self->io_offset < self->window_offset
137         || self->window_offset + self->window_size <= self->io_offset) {
138       /*
139        * io_offset is outside the window.  move the window so that
140        * it's within.
141        */
142       NaClLog(4, "Seek required\n");
143
144       new_window_offset = (self->io_offset
145                            & (~(((size_t) NACL_MAP_PAGESIZE) - 1)));
146       NaClLog(4, "new_window_offset 0x%"NACL_PRIxS"\n", new_window_offset);
147       CHECK(0 == (new_window_offset &
148                   (((size_t) NACL_MAP_PAGESIZE)-1)));
149       if (!NaClGioShmSetWindow(self, new_window_offset)) {
150         if (0 == sofar) {
151           errno = EIO;
152           sofar = -1;
153         }
154         return sofar;
155       }
156     } else {
157       NaClLog(4, "no seek required\n");
158     }
159     NaClLog(4, "   cur_window 0x%"NACL_PRIxPTR"\n",
160             (uintptr_t) self->cur_window);
161     NaClLog(4, "    io_offset 0x%"NACL_PRIxS"\n", self->io_offset);
162     NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset);
163
164     CHECK(self->window_offset <= self->io_offset);
165     CHECK(self->io_offset < self->window_offset + self->window_size);
166
167     transfer = count;
168     window_end = self->window_offset + self->window_size;
169     if (window_end > self->shm_sz) {
170       window_end = self->shm_sz;
171     }
172     window_remain = window_end - self->io_offset;
173
174     NaClLog(4, "remaining in window 0x%"NACL_PRIxS"\n", window_remain);
175
176     CHECK(window_remain <= GIO_SHM_WINDOWSIZE);
177
178     if (transfer > window_remain) {
179       transfer = window_remain;
180     }
181
182     NaClLog(4, "transfer 0x%"NACL_PRIxS"\n", transfer);
183
184     if (is_write) {
185       NaClLog(4,
186               ("about to \"write\" memcpy(0x%"NACL_PRIxPTR", "
187                " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"),
188               (uintptr_t) (self->cur_window
189                            + (self->io_offset - self->window_offset)),
190               (uintptr_t) buf,
191               transfer);
192
193       memcpy(self->cur_window + (self->io_offset - self->window_offset),
194              buf,
195              transfer);
196     } else {
197       NaClLog(4,
198               ("about to \"read\" memcpy(0x%"NACL_PRIxPTR", "
199                " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"),
200               (uintptr_t) buf,
201               (uintptr_t) (self->cur_window
202                            + (self->io_offset - self->window_offset)),
203               transfer);
204
205       memcpy(buf,
206              self->cur_window + (self->io_offset - self->window_offset),
207              transfer);
208     }
209     self->io_offset += transfer;
210     sofar += transfer;
211
212     buf = (void *)((uintptr_t) buf + transfer);
213     count -= transfer;
214   }
215
216   return sofar;
217 }
218
219 static ssize_t NaClGioShmRead(struct Gio *vself,
220                               void       *buf,
221                               size_t     count) {
222   return NaClGioShmReadOrWrite(vself, buf, count, 0);
223 }
224
225 static ssize_t NaClGioShmWrite(struct Gio *vself,
226                                const void *buf,
227                                size_t     count) {
228   return NaClGioShmReadOrWrite(vself, (void *) buf, count, 1);
229 }
230
231 static off_t NaClGioShmSeek(struct Gio  *vself,
232                             off_t       offset,
233                             int         whence) {
234   struct NaClGioShm *self = (struct NaClGioShm *) vself;
235   size_t new_pos = (size_t) -1;
236
237   NaClLog(4, "NaClGioShmSeek(0x%"NACL_PRIxPTR", %ld (0x%lx), %d)\n",
238           (uintptr_t) vself, (long) offset, (long) offset, whence);
239   /*
240    * Note that if sizeof(new_pos) < sizeof(offset), we are dropping
241    * high-order bits and we do not detect this.  However, the check
242    * after the switch keeps the values somewhat sane: we will never
243    * set the I/O offset to be outside the range [0, self->shm_sz].
244    */
245   switch (whence) {
246     case SEEK_SET:
247       new_pos = (size_t) offset;
248       break;
249     case SEEK_CUR:
250       new_pos = self->io_offset + offset;
251       break;
252     case SEEK_END:
253       new_pos = self->shm_sz + offset;
254       break;
255   }
256   /* allow equality, so setting to the end of file is okay */
257   if (self->shm_sz < new_pos) {
258     NaClLog(4, " invalid offset\n");
259     errno = EINVAL;
260     return -1;
261   }
262   NaClLog(4, " setting to %ld (0x%lx)\n", (long) new_pos, (long) new_pos);
263   /* sizeof(off_t) >= sizeof(size_t) */
264   self->io_offset = new_pos;
265   return (off_t) self->io_offset;
266 }
267
268 static int NaClGioShmFlush(struct Gio *vself) {
269   UNREFERENCED_PARAMETER(vself);
270   return 0;
271 }
272
273 static int NaClGioShmClose(struct Gio *vself) {
274   struct NaClGioShm *self = (struct NaClGioShm *) vself;
275
276   if (NULL != self->cur_window) {
277     NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window,
278                         NACL_MAP_PAGESIZE);
279   }
280   self->cur_window = NULL;
281
282   if (NULL == self->shmp) {
283     NaClLog(LOG_ERROR, "NaClGioShmClose: double close detected\n");
284     errno = EIO;
285     return -1;
286   }
287
288   NaClDescUnref(self->shmp);
289   self->shmp = NULL;  /* double close will fault */
290   return 0;
291 }
292
293 static void NaClGioShmDtor(struct Gio *vself) {
294   struct NaClGioShm *self = (struct NaClGioShm *) vself;
295
296   /*
297    * Users of Gio objects are expected to Close then Dtor, but Dtor
298    * should cleanup regardless.
299    */
300   if (NULL != self->shmp) {
301     if (-1 == (*vself->vtbl->Close)(vself)) {
302       NaClLog(LOG_ERROR, "NaClGioShmDtor: auto Close failed!\n");
303     }
304   }
305
306   self->shmp = NULL;
307   self->base.vtbl = NULL;
308 }
309
310 const struct GioVtbl kNaClGioShmVtbl = {
311   NaClGioShmDtor,
312   NaClGioShmRead,
313   NaClGioShmWrite,
314   NaClGioShmSeek,
315   NaClGioShmFlush,
316   NaClGioShmClose,
317 };
318
319
320 static int NaClGioShmCtorIntern(struct NaClGioShm  *self,
321                                 struct NaClDesc    *shmp,
322                                 size_t             shm_size) {
323   struct nacl_abi_stat  stbuf;
324   int                   vfret;
325   int                   rval = 0;
326
327   self->base.vtbl = NULL;
328
329   self->shmp = NULL;
330   self->cur_window = NULL;
331
332   if (0 != (vfret = (*((struct NaClDescVtbl const *) shmp->base.vtbl)->
333                      Fstat)(shmp, &stbuf))) {
334     NaClLog(1, "NaClGioShmCtorIntern: Fstat virtual function returned %d\n",
335             vfret);
336     goto cleanup;
337   }
338   /*
339    * nacl_abi_off_t is signed 32-bit quantity, but we don't want to
340    * hardwire in that knowledge here.
341    *
342    * size_t is unsigned, and may be 32-bits or 64-bits, depending on
343    * the underlying host OS.
344    *
345    * we want to ensure that the shm's size, as reported by the desc
346    * abstraction and thus is in nacl_abi_off_t, is at least that
347    * claimed by the ctor argument.  so, if (as Integers)
348    *
349    *  stbuf.nacl_abi_st_size < shm_size
350    *
351    * holds, this is an error.  however, the value-preserving cast rule
352    * makes this harder.
353    *
354    * Note that for signed sizes (ssize_t), the kernel ABI generally
355    * only reserve -1 for error, and asking for an I/O operation via a
356    * size_t that would succeed but yield a ssize_t return value that
357    * is negative is okay, since -1 is never valid as an I/O size on a
358    * von Neuman machine (except for a writev where the iov entries
359    * overlap): there just isn't that much data to read/write, when the
360    * instructions also take up space in the process address space.
361    * Whether requiring the programmer to detect this corner case is
362    * advisable is a different argument -- similar to negative ssize_t
363    * sizes, the syscall can just succeed with a partial transfer to
364    * avoid returning -1 on a success, just as we could avoid returning
365    * negative values; in practice, we do the latter, since we often
366    * see code written that tests for syscall error by comparing the
367    * return value to see if it is less than zero, rather than if it is
368    * equal to -1.
369    */
370   if (stbuf.nacl_abi_st_size < 0) {
371     NaClLog(LOG_ERROR, "NaClGioShmCtorIntern: actual shm size negative\n");
372     goto cleanup;
373   }
374   if (stbuf.nacl_abi_st_size <= (nacl_abi_off_t) SIZE_T_MAX
375       && (size_t) stbuf.nacl_abi_st_size < shm_size) {
376     NaClLog(LOG_ERROR,
377             "NaClGioShmCtorIntern: claimed shm file size greater than"
378             " actual shm segment size\n");
379     goto cleanup;
380   }
381   if (OFF_T_MAX < SIZE_T_MAX && (size_t) OFF_T_MAX < shm_size) {
382     NaClLog(LOG_ERROR,
383             ("NaClGioShmCtorIntern: claimed shm file size greater than"
384              " off_t max value, %"NACL_PRId64"\n"),
385             (int64_t) OFF_T_MAX);
386     goto cleanup;
387   }
388
389   self->shmp = NaClDescRef(shmp);
390
391   self->io_offset = 0;
392   self->shm_sz = shm_size;
393   self->window_offset = 0;
394
395   self->base.vtbl = &kNaClGioShmVtbl;
396
397   if (!NaClGioShmSetWindow(self, 0)) {
398     NaClLog(LOG_ERROR,
399             ("NaClGioShmCtorIntern: initial seek to beginning failed\n"));
400     NaClDescUnref(self->shmp);
401     self->shmp = NULL;
402     self->shm_sz = 0;
403     self->base.vtbl = NULL;
404     goto cleanup;
405   }
406
407   rval = 1;
408  cleanup:
409   return rval;
410 }
411
412 int NaClGioShmCtor(struct NaClGioShm  *self,
413                    struct NaClDesc    *shmp,
414                    size_t             shm_size) {
415
416   int rv;
417
418   CHECK(shm_size == NaClRoundAllocPage(shm_size));
419
420   rv = NaClGioShmCtorIntern(self, shmp, shm_size);
421
422   return rv;
423 }
424
425 int NaClGioShmAllocCtor(struct NaClGioShm *self,
426                         size_t            shm_size) {
427   struct NaClDescImcShm *shmp;
428   int                   rv;
429
430   CHECK(shm_size == NaClRoundAllocPage(shm_size));
431
432   shmp = malloc(sizeof *shmp);
433   if (NULL == shmp) {
434     return 0;
435   }
436   if (!NaClDescImcShmAllocCtor(shmp, shm_size, /* executable= */ 0)) {
437     free(shmp);
438     return 0;
439   }
440
441   rv = NaClGioShmCtorIntern(self, (struct NaClDesc *) shmp, shm_size);
442   NaClDescUnref((struct NaClDesc *) shmp);
443
444   if (!rv) {
445     free(shmp);
446   }
447   return rv;
448 }