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 : /*
8 : * Native Client Resource Descriptor Transfer protocol for trusted code.
9 : */
10 :
11 : #include <sys/types.h>
12 : #include <sys/stat.h>
13 : #include <fcntl.h>
14 : #include <string.h>
15 :
16 : #include "native_client/src/include/portability.h"
17 : #include "native_client/src/include/nacl_macros.h"
18 :
19 : #include "native_client/src/public/imc_types.h"
20 :
21 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
22 : #include "native_client/src/trusted/desc/nacl_desc_cond.h"
23 : #include "native_client/src/trusted/desc/nacl_desc_conn_cap.h"
24 : #include "native_client/src/trusted/desc/nacl_desc_effector.h"
25 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
26 : #include "native_client/src/trusted/desc/nacl_desc_imc.h"
27 : #include "native_client/src/trusted/desc/nacl_desc_imc_bound_desc.h"
28 : #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
29 : #include "native_client/src/trusted/desc/nacl_desc_mutex.h"
30 : #include "native_client/src/trusted/desc/nacl_desc_dir.h"
31 : #include "native_client/src/trusted/desc/nrd_xfer.h"
32 : #include "native_client/src/trusted/desc/nrd_xfer_intern.h"
33 :
34 : #include "native_client/src/shared/platform/nacl_global_secure_random.h"
35 : #include "native_client/src/shared/platform/nacl_log.h"
36 :
37 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
38 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
39 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
40 :
41 :
42 : /*
43 : * The NRD xfer header that describes the types of NRDs being
44 : * transferred should be rounded to a multiple of 16 bytes in length.
45 : * This allows fast, SSE-based memcpy routines to work, giving us
46 : * 16-bytes-per-cycle memory copies for in-cache data.
47 : */
48 :
49 : static INLINE size_t min_size(size_t a,
50 4 : size_t b) {
51 4 : if (a < b) return a;
52 4 : return b;
53 4 : }
54 :
55 : void NaClNrdXferIncrTagOverhead(size_t *byte_count,
56 0 : size_t *handle_count) {
57 : UNREFERENCED_PARAMETER(handle_count);
58 0 : ++*byte_count;
59 0 : }
60 :
61 2 : enum NaClDescTypeTag NaClNrdXferReadTypeTag(struct NaClDescXferState *xferp) {
62 2 : return (enum NaClDescTypeTag) (0xff & *xferp->next_byte++);
63 2 : }
64 :
65 : void NaClNrdXferWriteTypeTag(struct NaClDescXferState *xferp,
66 2 : struct NaClDesc *descp) {
67 2 : *xferp->next_byte++ = NACL_VTBL(NaClDesc, descp)->typeTag;
68 2 : }
69 :
70 : /*
71 : * Returns number of descriptors internalized, or an error code.
72 : * Since only one descriptor can be internalized (out parameter is not
73 : * an array), this means 0 or 1 are non-error conditions.
74 : *
75 : * Returns negative errno (syscall-style) on error.
76 : */
77 : int NaClDescInternalizeFromXferBuffer(
78 : struct NaClDesc **out_desc,
79 : struct NaClDescXferState *xferp,
80 2 : struct NaClDescQuotaInterface *quota_interface) {
81 : int xfer_status;
82 : size_t type_tag;
83 :
84 2 : type_tag = NaClNrdXferReadTypeTag(xferp);
85 : /* 0 <= type_tag */
86 2 : if (NACL_DESC_TYPE_END_TAG == type_tag) {
87 2 : return 0;
88 : }
89 2 : if (type_tag >= NACL_DESC_TYPE_MAX) {
90 : NaClLog(4, ("illegal type tag %"NACL_PRIuS" (0x%"NACL_PRIxS")\n"),
91 0 : type_tag, type_tag);
92 0 : return -NACL_ABI_EIO;
93 : }
94 : if ((int (*)(struct NaClDesc **, struct NaClDescXferState *,
95 : struct NaClDescQuotaInterface *)) NULL ==
96 2 : NaClDescInternalize[type_tag]) {
97 : NaClLog(LOG_FATAL,
98 : "No internalization function for type %"NACL_PRIuS"\n",
99 0 : type_tag);
100 : /* fatal, but in case we change it later */
101 0 : return -NACL_ABI_EIO;
102 : }
103 : xfer_status = (*NaClDescInternalize[type_tag])(out_desc, xferp,
104 2 : quota_interface);
105 : /* constructs new_desc, transferring ownership of any handles consumed */
106 :
107 2 : if (xfer_status != 0) {
108 : NaClLog(0,
109 : "non-zero xfer_status %d, desc type tag %s (%"NACL_PRIuS")\n",
110 : xfer_status,
111 : NaClDescTypeString(type_tag),
112 0 : type_tag);
113 : }
114 2 : return 0 == xfer_status;
115 2 : }
116 :
117 : int NaClDescExternalizeToXferBuffer(struct NaClDescXferState *xferp,
118 2 : struct NaClDesc *out) {
119 : /*
120 : * Externalize should expose the object as NaClHandles that must
121 : * be sent, but no ownership is transferred. The NaClHandle
122 : * objects cannot be deallocated until after the actual
123 : * LowLevelSendMsg completes, so multithreaded code beware! By
124 : * using NaClDescRef and NaClDescUnref properly, there shouldn't be
125 : * any problems, since all entries in the ndescv should have had
126 : * their refcount incremented, and the NaClHandles will not be
127 : * closed until the NaClDesc objects' refcount goes to zero.
128 : */
129 2 : NaClNrdXferWriteTypeTag(xferp, out);
130 2 : return (*NACL_VTBL(NaClDesc, out)->Externalize)(out, xferp);
131 2 : }
132 :
133 : ssize_t NaClImcSendTypedMessage(struct NaClDesc *channel,
134 : const struct NaClImcTypedMsgHdr *nitmhp,
135 4 : int flags) {
136 : int supported_flags;
137 4 : ssize_t retval = -NACL_ABI_EINVAL;
138 : struct NaClMessageHeader kern_msg_hdr; /* xlated interface */
139 : size_t i;
140 : /*
141 : * BEWARE: NaClImcMsgIoVec has the same layout as NaClIOVec, so
142 : * there will be type punning below to avoid copying from a struct
143 : * NaClImcMsgIoVec array to a struct NaClIOVec array in order to
144 : * call the IMC library code using kern_msg_hdr.
145 : */
146 : struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX + 1];
147 : struct NaClDesc **kern_desc;
148 : NaClHandle kern_handle[NACL_ABI_IMC_DESC_MAX];
149 : size_t user_bytes;
150 : size_t sys_bytes;
151 : size_t sys_handles;
152 : size_t desc_bytes;
153 : size_t desc_handles;
154 : struct NaClInternalHeader *hdr;
155 : char *hdr_buf;
156 : struct NaClDescXferState xfer_state;
157 :
158 : static struct NaClInternalHeader const kNoHandles = {
159 : { NACL_HANDLE_TRANSFER_PROTOCOL, 0, },
160 : /* and implicit zeros for pad bytes */
161 : };
162 :
163 : NaClLog(3,
164 : ("Entered"
165 : " NaClImcSendTypedMessage(0x%08"NACL_PRIxPTR", "
166 : "0x%08"NACL_PRIxPTR", 0x%x)\n"),
167 4 : (uintptr_t) channel, (uintptr_t) nitmhp, flags);
168 : /*
169 : * What flags do we know now? If a program was compiled using a
170 : * newer ABI than what this implementation knows, the extra flag
171 : * bits are ignored but will generate a warning.
172 : */
173 4 : supported_flags = NACL_ABI_IMC_NONBLOCK;
174 4 : if (0 != (flags & ~supported_flags)) {
175 : NaClLog(LOG_WARNING,
176 : "WARNING: NaClImcSendTypedMessage: unknown IMC flag used: 0x%x\n",
177 0 : flags);
178 0 : flags &= supported_flags;
179 : }
180 :
181 : /*
182 : * use (ahem) RTTI -- or a virtual function that's been partially
183 : * evaluated/memoized -- to short circuit the error check, so that
184 : * cleanups are easier (rather than letting it fail at the
185 : * ExternalizeSize virtual function call).
186 : */
187 : if (0 != nitmhp->ndesc_length
188 4 : && NACL_DESC_IMC_SOCKET != NACL_VTBL(NaClDesc, channel)->typeTag) {
189 0 : NaClLog(4, "not an IMC socket and trying to send descriptors!\n");
190 0 : return -NACL_ABI_EINVAL;
191 : }
192 :
193 4 : if (nitmhp->iov_length > NACL_ABI_IMC_IOVEC_MAX) {
194 0 : NaClLog(4, "gather/scatter array too large\n");
195 0 : return -NACL_ABI_EINVAL;
196 : }
197 4 : if (nitmhp->ndesc_length > NACL_ABI_IMC_USER_DESC_MAX) {
198 0 : NaClLog(4, "handle vector too long\n");
199 0 : return -NACL_ABI_EINVAL;
200 : }
201 :
202 : memcpy(kern_iov + 1, (void *) nitmhp->iov,
203 4 : nitmhp->iov_length * sizeof *nitmhp->iov);
204 : /*
205 : * kern_iov[0] is the message header that tells the recipient where
206 : * the user data and capabilities data (e.g., NaClDescConnCap)
207 : * boundary is, as well as types of objects in the handles vector of
208 : * NaClHandle objects, which must be internalized.
209 : *
210 : * NB: we assume that communication is secure, and no external
211 : * entity can listen in or will inject bogus information. This is a
212 : * strong assumption, since falsification can cause loss of
213 : * type-safety.
214 : */
215 4 : user_bytes = 0;
216 4 : for (i = 0; i < nitmhp->iov_length; ++i) {
217 3 : if (user_bytes > SIZE_T_MAX - kern_iov[i+1].length) {
218 0 : return -NACL_ABI_EINVAL;
219 : }
220 3 : user_bytes += kern_iov[i+1].length;
221 3 : }
222 4 : if (user_bytes > NACL_ABI_IMC_USER_BYTES_MAX) {
223 0 : return -NACL_ABI_EINVAL;
224 : }
225 :
226 4 : kern_desc = nitmhp->ndescv;
227 4 : hdr_buf = NULL;
228 :
229 : /*
230 : * NOTE: type punning w/ NaClImcMsgIoVec and NaClIOVec.
231 : * This breaks ansi type aliasing rules and hence we may use
232 : * soemthing like '-fno-strict-aliasing'
233 : */
234 4 : kern_msg_hdr.iov = (struct NaClIOVec *) kern_iov;
235 4 : kern_msg_hdr.iov_length = nitmhp->iov_length + 1; /* header */
236 :
237 4 : if (0 == nitmhp->ndesc_length) {
238 3 : kern_msg_hdr.handles = NULL;
239 3 : kern_msg_hdr.handle_count = 0;
240 3 : kern_iov[0].base = (void *) &kNoHandles;
241 3 : kern_iov[0].length = sizeof kNoHandles;
242 3 : } else {
243 : /*
244 : * \forall i \in [0, nitmhp->desc_length): kern_desc[i] != NULL.
245 : */
246 :
247 2 : sys_bytes = 0;
248 2 : sys_handles = 0;
249 : /* nitmhp->desc_length <= NACL_ABI_IMC_USER_DESC_MAX */
250 2 : for (i = 0; i < nitmhp->ndesc_length; ++i) {
251 2 : desc_bytes = 0;
252 2 : desc_handles = 0;
253 : retval = (*((struct NaClDescVtbl const *) kern_desc[i]->base.vtbl)->
254 : ExternalizeSize)(kern_desc[i],
255 : &desc_bytes,
256 2 : &desc_handles);
257 2 : if (retval < 0) {
258 : NaClLog(1,
259 : ("NaClImcSendTypedMessage: ExternalizeSize"
260 : " returned %"NACL_PRIdS"\n"),
261 0 : retval);
262 0 : goto cleanup;
263 : }
264 : /*
265 : * No integer overflow should be possible given the max-handles
266 : * limits and actual resource use per handle involved.
267 : */
268 : if (desc_bytes > NACL_ABI_SIZE_T_MAX - 1
269 : || (desc_bytes + 1) > NACL_ABI_SIZE_T_MAX - sys_bytes
270 2 : || desc_handles > NACL_ABI_SIZE_T_MAX - sys_handles) {
271 0 : retval = -NACL_ABI_EOVERFLOW;
272 0 : goto cleanup;
273 : }
274 2 : sys_bytes += (1 + desc_bytes);
275 2 : sys_handles += desc_handles;
276 2 : }
277 2 : if (sys_handles > NACL_ABI_IMC_DESC_MAX) {
278 : NaClLog(LOG_FATAL, ("User had %"NACL_PRIdNACL_SIZE" descriptors,"
279 : " which expanded into %"NACL_PRIuS
280 : "handles, more than"
281 : " the max of %d.\n"),
282 : nitmhp->ndesc_length, sys_handles,
283 0 : NACL_ABI_IMC_DESC_MAX);
284 : }
285 : /* a byte for NACL_DESC_TYPE_END_TAG, then rounded up to 0 mod 16 */
286 2 : sys_bytes = (sys_bytes + 1 + 0xf) & ~0xf;
287 :
288 : /* bsy notes that sys_bytes should never exceed NACL_ABI_IMC_DESC_MAX
289 : * times the maximum protocol transfer size, which is currently set to
290 : * NACL_PATH_MAX, so it would be abnormal for this check to fail.
291 : * Including it anyway just in case something changes.
292 : */
293 2 : if (sys_bytes > NACL_ABI_SIZE_T_MAX - sizeof *hdr) {
294 : NaClLog(LOG_FATAL, "NaClImcSendTypedMessage: "
295 : "Buffer size overflow (%"NACL_PRIuS" bytes)",
296 0 : sys_bytes);
297 0 : retval = -NACL_ABI_EOVERFLOW;
298 0 : goto cleanup;
299 : }
300 2 : hdr_buf = malloc(sys_bytes + sizeof *hdr);
301 2 : if (NULL == hdr_buf) {
302 0 : NaClLog(4, "NaClImcSendTypedMessage: out of memory for iov");
303 0 : retval = -NACL_ABI_ENOMEM;
304 0 : goto cleanup;
305 : }
306 2 : kern_iov[0].base = (void *) hdr_buf;
307 :
308 : /* Checked above that sys_bytes <= NACL_ABI_SIZE_T_MAX - sizeof *hdr */
309 2 : kern_iov[0].length = (nacl_abi_size_t)(sys_bytes + sizeof *hdr);
310 :
311 2 : hdr = (struct NaClInternalHeader *) hdr_buf;
312 2 : memset(hdr, 0, sizeof(*hdr)); /* Initilize the struct's padding bytes. */
313 2 : hdr->h.xfer_protocol_version = NACL_HANDLE_TRANSFER_PROTOCOL;
314 2 : if (sys_bytes > UINT32_MAX) {
315 : /*
316 : * We really want:
317 : * sys_bytes > numeric_limits<typeof(hdr->h.descriptor_data_bytes)>:max()
318 : * in case the type changes.
319 : *
320 : * This should never occur in practice, since
321 : * NACL_ABI_IMC_DESC_MAX * NACL_PATH_MAX is far smaller than
322 : * UINT32_MAX. Furthermore, NACL_ABI_IMC_DESC_MAX *
323 : * NACL_PATH_MAX + user_bytes <= NACL_ABI_IMC_DESC_MAX *
324 : * NACL_PATH_MAX + NACL_ABI_IMC_USER_BYTES_MAX == max_xfer (so
325 : * max_xfer is upper bound on data transferred), and max_xfer <=
326 : * INT32_MAX = NACL_ABI_SSIZE_MAX <= SSIZE_T_MAX holds.
327 : */
328 0 : retval = -NACL_ABI_EOVERFLOW;
329 0 : goto cleanup;
330 : }
331 2 : hdr->h.descriptor_data_bytes = (uint32_t) sys_bytes;
332 :
333 2 : xfer_state.next_byte = (char *) (hdr + 1);
334 2 : xfer_state.byte_buffer_end = xfer_state.next_byte + sys_bytes;
335 2 : xfer_state.next_handle = kern_handle;
336 : xfer_state.handle_buffer_end = xfer_state.next_handle
337 2 : + NACL_ABI_IMC_DESC_MAX;
338 :
339 2 : for (i = 0; i < nitmhp->ndesc_length; ++i) {
340 2 : retval = NaClDescExternalizeToXferBuffer(&xfer_state, kern_desc[i]);
341 2 : if (0 != retval) {
342 : NaClLog(4,
343 : ("NaClImcSendTypedMessage: Externalize for"
344 : " descriptor %"NACL_PRIuS" returned %"NACL_PRIdS"\n"),
345 0 : i, retval);
346 0 : goto cleanup;
347 : }
348 2 : }
349 2 : *xfer_state.next_byte++ = (char) NACL_DESC_TYPE_END_TAG;
350 : /*
351 : * zero fill the rest of memory to avoid leaking info from
352 : * otherwise uninitialized malloc'd memory.
353 : */
354 2 : while (xfer_state.next_byte < xfer_state.byte_buffer_end) {
355 2 : *xfer_state.next_byte++ = '\0';
356 2 : }
357 :
358 2 : kern_msg_hdr.handles = kern_handle;
359 2 : kern_msg_hdr.handle_count = (uint32_t) sys_handles;
360 : }
361 :
362 4 : NaClLog(4, "Invoking LowLevelSendMsg, flags 0x%x\n", flags);
363 :
364 : retval = (*((struct NaClDescVtbl const *) channel->base.vtbl)->
365 4 : LowLevelSendMsg)(channel, &kern_msg_hdr, flags);
366 4 : NaClLog(4, "LowLevelSendMsg returned %"NACL_PRIdS"\n", retval);
367 4 : if (NaClSSizeIsNegErrno(&retval)) {
368 : /*
369 : * NaClWouldBlock uses TSD (for both the errno-based and
370 : * GetLastError()-based implementations), so this is threadsafe.
371 : */
372 0 : if (0 != (flags & NACL_DONT_WAIT) && NaClWouldBlock()) {
373 0 : retval = -NACL_ABI_EAGAIN;
374 0 : } else if (-NACL_ABI_EMSGSIZE == retval) {
375 : /*
376 : * Allow the above layer to process when imc_sendmsg calls fail due
377 : * to the OS not supporting a large enough buffer.
378 : */
379 0 : retval = -NACL_ABI_EMSGSIZE;
380 0 : } else {
381 : /*
382 : * TODO(bsy): the else case is some mysterious internal error.
383 : * should we destroy the channel? Was the failure atomic? Did
384 : * it send some partial data? Linux implementation appears
385 : * okay.
386 : *
387 : * We return EIO and let the caller deal with it.
388 : */
389 0 : retval = -NACL_ABI_EIO;
390 0 : }
391 4 : } else if ((unsigned) retval < kern_iov[0].length) {
392 : /*
393 : * retval >= 0, so cast to unsigned is value preserving.
394 : */
395 0 : retval = -NACL_ABI_ENOBUFS;
396 0 : } else {
397 : /*
398 : * The return value (number of bytes sent) should not include the
399 : * "out of band" additional information added by the service runtime.
400 : */
401 4 : retval -= kern_iov[0].length;
402 : }
403 :
404 : cleanup:
405 :
406 4 : free(hdr_buf);
407 :
408 4 : NaClLog(4, "NaClImcSendTypedMessage: returning %"NACL_PRIdS"\n", retval);
409 :
410 4 : return retval;
411 4 : }
412 :
413 :
414 : ssize_t NaClImcRecvTypedMessage(
415 : struct NaClDesc *channel,
416 : struct NaClImcTypedMsgHdr *nitmhp,
417 : int flags,
418 4 : struct NaClDescQuotaInterface *quota_interface) {
419 : int supported_flags;
420 : ssize_t retval;
421 : char *recv_buf;
422 : size_t user_bytes;
423 : NaClHandle kern_handle[NACL_ABI_IMC_DESC_MAX];
424 : struct NaClIOVec recv_iov;
425 : struct NaClMessageHeader recv_hdr;
426 : ssize_t total_recv_bytes;
427 : struct NaClInternalHeader intern_hdr;
428 : size_t recv_user_bytes_avail;
429 : size_t tmp;
430 : char *user_data;
431 : size_t iov_copy_size;
432 : struct NaClDescXferState xfer;
433 : struct NaClDesc *new_desc[NACL_ABI_IMC_DESC_MAX];
434 : int xfer_status;
435 : size_t i;
436 : size_t num_user_desc;
437 :
438 : NaClLog(4,
439 : "Entered NaClImcRecvTypedMsg(0x%08"NACL_PRIxPTR", "
440 : "0x%08"NACL_PRIxPTR", %d)\n",
441 4 : (uintptr_t) channel, (uintptr_t) nitmhp, flags);
442 :
443 4 : supported_flags = NACL_ABI_IMC_NONBLOCK;
444 4 : if (0 != (flags & ~supported_flags)) {
445 : NaClLog(LOG_WARNING,
446 : "WARNING: NaClImcRecvTypedMsg: unknown IMC flag used: 0x%x\n",
447 0 : flags);
448 0 : flags &= supported_flags;
449 : }
450 :
451 4 : if (nitmhp->iov_length > NACL_ABI_IMC_IOVEC_MAX) {
452 0 : NaClLog(4, "gather/scatter array too large\n");
453 0 : return -NACL_ABI_EINVAL;
454 : }
455 4 : if (nitmhp->ndesc_length > NACL_ABI_IMC_USER_DESC_MAX) {
456 0 : NaClLog(4, "handle vector too long\n");
457 0 : return -NACL_ABI_EINVAL;
458 : }
459 :
460 4 : user_bytes = 0;
461 4 : for (i = 0; i < nitmhp->iov_length; ++i) {
462 3 : if (user_bytes > SIZE_T_MAX - nitmhp->iov[i].length) {
463 0 : NaClLog(4, "integer overflow in iov length summation\n");
464 0 : return -NACL_ABI_EINVAL;
465 : }
466 3 : user_bytes += nitmhp->iov[i].length;
467 3 : }
468 : /*
469 : * if user_bytes > NACL_ABI_IMC_USER_BYTES_MAX,
470 : * we will just never fill up all the buffer space.
471 : */
472 4 : user_bytes = min_size(user_bytes, NACL_ABI_IMC_USER_BYTES_MAX);
473 : /*
474 : * user_bytes = \min(\sum_{i=0}{nitmhp->iov_length-1} nitmhp->iov[i].length,
475 : * NACL_ABI_IMC_USER_BYTES_MAX)
476 : */
477 :
478 4 : recv_buf = NULL;
479 4 : memset(new_desc, 0, sizeof new_desc);
480 : /*
481 : * from here on, set retval and jump to cleanup code.
482 : */
483 :
484 4 : recv_buf = malloc(NACL_ABI_IMC_BYTES_MAX);
485 4 : if (NULL == recv_buf) {
486 0 : NaClLog(4, "no memory for receive buffer\n");
487 0 : retval = -NACL_ABI_ENOMEM;
488 0 : goto cleanup;
489 : }
490 :
491 4 : recv_iov.base = (void *) recv_buf;
492 4 : recv_iov.length = NACL_ABI_IMC_BYTES_MAX;
493 :
494 4 : recv_hdr.iov = &recv_iov;
495 4 : recv_hdr.iov_length = 1;
496 :
497 4 : for (i = 0; i < NACL_ARRAY_SIZE(kern_handle); ++i) {
498 4 : kern_handle[i] = NACL_INVALID_HANDLE;
499 4 : }
500 :
501 : if (NACL_DESC_IMC_SOCKET == ((struct NaClDescVtbl const *)
502 4 : channel->base.vtbl)->typeTag) {
503 : /*
504 : * Channel can transfer access rights.
505 : */
506 :
507 4 : recv_hdr.handles = kern_handle;
508 4 : recv_hdr.handle_count = NACL_ARRAY_SIZE(kern_handle);
509 4 : NaClLog(4, "Connected socket, may transfer descriptors\n");
510 4 : } else {
511 : /*
512 : * Channel cannot transfer access rights. The syscall would fail
513 : * if recv_iov.length is non-zero.
514 : */
515 :
516 0 : recv_hdr.handles = (NaClHandle *) NULL;
517 0 : recv_hdr.handle_count = 0;
518 0 : NaClLog(4, "Transferable Data Only socket\n");
519 : }
520 :
521 4 : recv_hdr.flags = 0; /* just to make it obvious; IMC will clear it for us */
522 :
523 : total_recv_bytes = (*((struct NaClDescVtbl const *) channel->base.vtbl)->
524 : LowLevelRecvMsg)(channel,
525 : &recv_hdr,
526 4 : flags);
527 4 : if (NaClSSizeIsNegErrno(&total_recv_bytes)) {
528 : NaClLog(1, "LowLevelRecvMsg failed, returned %"NACL_PRIdS"\n",
529 2 : total_recv_bytes);
530 2 : retval = total_recv_bytes;
531 2 : goto cleanup;
532 : }
533 : /* total_recv_bytes >= 0 */
534 :
535 : /*
536 : * NB: recv_hdr.flags may already contain NACL_ABI_MESSAGE_TRUNCATED
537 : * and/or NACL_ABI_HANDLES_TRUNCATED.
538 : *
539 : * First, parse the NaClInternalHeader and any subsequent fields to
540 : * extract and internalize the NaClDesc objects from the array of
541 : * NaClHandle values.
542 : *
543 : * Copy out to user buffer. Possibly additional truncation may occur.
544 : *
545 : * Since total_recv_bytes >= 0, the cast to size_t is value preserving.
546 : */
547 4 : if ((size_t) total_recv_bytes < sizeof intern_hdr) {
548 : NaClLog(4, ("only received %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes,"
549 : " but internal header is %"NACL_PRIuS" (0x%"NACL_PRIxS
550 : ") bytes\n"),
551 : total_recv_bytes, (size_t) total_recv_bytes,
552 0 : sizeof intern_hdr, sizeof intern_hdr);
553 0 : retval = -NACL_ABI_EIO;
554 0 : goto cleanup;
555 : }
556 4 : memcpy(&intern_hdr, recv_buf, sizeof intern_hdr);
557 : /*
558 : * Future code should handle old versions in a backward compatible way.
559 : */
560 4 : if (NACL_HANDLE_TRANSFER_PROTOCOL != intern_hdr.h.xfer_protocol_version) {
561 : NaClLog(4, ("protocol version mismatch:"
562 : " got %x, but can only handle %x\n"),
563 0 : intern_hdr.h.xfer_protocol_version, NACL_HANDLE_TRANSFER_PROTOCOL);
564 : /*
565 : * The returned value should be a special version mismatch error
566 : * code that, along with the recv_buf, permit retrying with later
567 : * decoders.
568 : */
569 0 : retval = -NACL_ABI_EIO;
570 0 : goto cleanup;
571 : }
572 : if ((size_t) total_recv_bytes < (intern_hdr.h.descriptor_data_bytes
573 4 : + sizeof intern_hdr)) {
574 : NaClLog(4, ("internal header (size %"NACL_PRIuS" (0x%"NACL_PRIxS")) "
575 : "says there are "
576 : "%d (0x%x) NRD xfer descriptor bytes, "
577 : "but we received %"NACL_PRIdS" (0x%"NACL_PRIxS") bytes\n"),
578 : sizeof intern_hdr, sizeof intern_hdr,
579 : intern_hdr.h.descriptor_data_bytes,
580 : intern_hdr.h.descriptor_data_bytes,
581 0 : total_recv_bytes, (size_t) total_recv_bytes);
582 0 : retval = -NACL_ABI_EIO;
583 0 : goto cleanup;
584 : }
585 : recv_user_bytes_avail = (total_recv_bytes
586 : - intern_hdr.h.descriptor_data_bytes
587 4 : - sizeof intern_hdr);
588 : /*
589 : * NaCl app asked for user_bytes, and we have recv_user_bytes_avail.
590 : * Set recv_user_bytes_avail to the min of these two values, as well
591 : * as inform the caller if data truncation occurred.
592 : */
593 4 : if (user_bytes < recv_user_bytes_avail) {
594 1 : recv_hdr.flags |= NACL_ABI_RECVMSG_DATA_TRUNCATED;
595 : }
596 4 : recv_user_bytes_avail = min_size(recv_user_bytes_avail, user_bytes);
597 :
598 4 : retval = recv_user_bytes_avail; /* default from hence forth */
599 :
600 : /*
601 : * Let UserDataSize := recv_user_bytes_avail. (bind to current value)
602 : */
603 :
604 4 : user_data = recv_buf + sizeof intern_hdr + intern_hdr.h.descriptor_data_bytes;
605 : /*
606 : * Let StartUserData := user_data
607 : */
608 :
609 : /*
610 : * Precondition: user_data in [StartUserData, StartUserData + UserDataSize].
611 : *
612 : * Invariant:
613 : * user_data + recv_user_bytes_avail == StartUserData + UserDataSize
614 : */
615 4 : for (i = 0; i < nitmhp->iov_length && 0 < recv_user_bytes_avail; ++i) {
616 3 : iov_copy_size = min_size(nitmhp->iov[i].length, recv_user_bytes_avail);
617 :
618 3 : memcpy(nitmhp->iov[i].base, user_data, iov_copy_size);
619 :
620 3 : user_data += iov_copy_size;
621 : /*
622 : * subtraction could not underflow due to how recv_user_bytes_avail was
623 : * computed; however, we are paranoid, in case the code changes.
624 : */
625 3 : tmp = recv_user_bytes_avail - iov_copy_size;
626 3 : if (tmp > recv_user_bytes_avail) {
627 : NaClLog(LOG_FATAL,
628 0 : "NaClImcRecvTypedMessage: impossible underflow occurred");
629 : }
630 3 : recv_user_bytes_avail = tmp;
631 :
632 3 : }
633 : /*
634 : * postcondition: recv_user_bytes_avail == 0.
635 : *
636 : * NB: 0 < recv_user_bytes_avail \rightarrow i < nitmhp->iov_length
637 : * must hold, due to how user_bytes is computed. We leave the
638 : * unnecessary test in the loop condition to avoid future code
639 : * changes from causing problems as defensive programming.
640 : */
641 :
642 : /*
643 : * Now extract/internalize the NaClHandles as NaClDesc objects.
644 : * Note that we will extract beyond nitmhp->desc_length, since we
645 : * must still destroy the ones that are dropped.
646 : */
647 4 : xfer.next_byte = recv_buf + sizeof intern_hdr;
648 4 : xfer.byte_buffer_end = xfer.next_byte + intern_hdr.h.descriptor_data_bytes;
649 4 : xfer.next_handle = kern_handle;
650 4 : xfer.handle_buffer_end = kern_handle + recv_hdr.handle_count;
651 :
652 4 : i = 0;
653 4 : while (xfer.next_byte < xfer.byte_buffer_end) {
654 : struct NaClDesc *out;
655 :
656 : xfer_status = NaClDescInternalizeFromXferBuffer(&out, &xfer,
657 2 : quota_interface);
658 2 : NaClLog(4, "NaClDescInternalizeFromXferBuffer: returned %d\n", xfer_status);
659 2 : if (0 == xfer_status) {
660 : /* end of descriptors reached */
661 2 : break;
662 : }
663 2 : if (i >= NACL_ARRAY_SIZE(new_desc)) {
664 : NaClLog(LOG_FATAL,
665 : ("NaClImcRecvTypedMsg: trusted peer tried to send too many"
666 0 : " descriptors!\n"));
667 : }
668 2 : if (1 != xfer_status) {
669 : /* xfer_status < 0, out did not receive output */
670 0 : retval = -NACL_ABI_EIO;
671 0 : goto cleanup;
672 : }
673 2 : new_desc[i] = out;
674 2 : out = NULL;
675 2 : ++i;
676 2 : }
677 4 : num_user_desc = i; /* actual number of descriptors received */
678 4 : if (nitmhp->ndesc_length < num_user_desc) {
679 1 : nitmhp->flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED;
680 1 : num_user_desc = nitmhp->ndesc_length;
681 : }
682 :
683 : /* transfer ownership to nitmhp->ndescv; some may be left behind */
684 4 : for (i = 0; i < num_user_desc; ++i) {
685 2 : nitmhp->ndescv[i] = new_desc[i];
686 2 : new_desc[i] = NULL;
687 2 : }
688 :
689 : /* cast is safe because we clamped num_user_desc earlier to
690 : * be no greater than the original value of nithmp->ndesc_length.
691 : */
692 4 : nitmhp->ndesc_length = (nacl_abi_size_t)num_user_desc;
693 :
694 : /* retval is number of bytes received */
695 :
696 : cleanup:
697 4 : free(recv_buf);
698 :
699 : /*
700 : * Note that we must exercise discipline when constructing NaClDesc
701 : * objects from NaClHandles -- the NaClHandle values *must* be set
702 : * to NACL_INVALID_HANDLE after the construction of the NaClDesc
703 : * where ownership of the NaClHandle is transferred into the NaCDesc
704 : * object. Otherwise, between new_desc and kern_handle cleanup code,
705 : * a NaClHandle might be closed twice.
706 : */
707 4 : for (i = 0; i < NACL_ARRAY_SIZE(new_desc); ++i) {
708 4 : if (NULL != new_desc[i]) {
709 1 : NaClDescUnref(new_desc[i]);
710 1 : new_desc[i] = NULL;
711 : }
712 4 : }
713 4 : for (i = 0; i < NACL_ARRAY_SIZE(kern_handle); ++i) {
714 4 : if (NACL_INVALID_HANDLE != kern_handle[i]) {
715 0 : (void) NaClClose(kern_handle[i]);
716 : }
717 4 : }
718 :
719 4 : NaClLog(3, "NaClImcRecvTypedMsg: returning %"NACL_PRIdS"\n", retval);
720 4 : return retval;
721 4 : }
722 :
723 0 : int32_t NaClCommonDescSocketPair(struct NaClDesc *pair[2]) {
724 0 : int32_t retval = -NACL_ABI_EIO;
725 : struct NaClDescXferableDataDesc *d0;
726 : struct NaClDescXferableDataDesc *d1;
727 : NaClHandle sock_pair[2];
728 :
729 : /*
730 : * mark resources to enable easy cleanup
731 : */
732 0 : d0 = NULL;
733 0 : d1 = NULL;
734 0 : sock_pair[0] = NACL_INVALID_HANDLE;
735 0 : sock_pair[1] = NACL_INVALID_HANDLE;
736 :
737 0 : if (0 != NaClSocketPair(sock_pair)) {
738 : NaClLog(1,
739 0 : "NaClCommonSysImc_Socket_Pair: IMC socket pair creation failed\n");
740 0 : retval = -NACL_ABI_ENFILE;
741 0 : goto cleanup;
742 : }
743 0 : if (NULL == (d0 = malloc(sizeof *d0))) {
744 0 : retval = -NACL_ABI_ENOMEM;
745 0 : goto cleanup;
746 : }
747 0 : if (NULL == (d1 = malloc(sizeof *d1))) {
748 0 : free((void *) d0);
749 0 : d0 = NULL;
750 0 : retval = -NACL_ABI_ENOMEM;
751 0 : goto cleanup;
752 : }
753 0 : if (!NaClDescXferableDataDescCtor(d0, sock_pair[0])) {
754 0 : free((void *) d0);
755 0 : d0 = NULL;
756 0 : free((void *) d1);
757 0 : d1 = NULL;
758 0 : retval = -NACL_ABI_ENFILE;
759 0 : goto cleanup;
760 : }
761 0 : sock_pair[0] = NACL_INVALID_HANDLE; /* ctor took ownership */
762 0 : if (!NaClDescXferableDataDescCtor(d1, sock_pair[1])) {
763 0 : free((void *) d1);
764 0 : d1 = NULL;
765 0 : retval = -NACL_ABI_ENFILE;
766 0 : goto cleanup;
767 : }
768 0 : sock_pair[1] = NACL_INVALID_HANDLE; /* ctor took ownership */
769 :
770 0 : pair[0] = (struct NaClDesc *) d0;
771 0 : d0 = NULL;
772 :
773 0 : pair[1] = (struct NaClDesc *) d1;
774 0 : d1 = NULL;
775 :
776 0 : retval = 0;
777 :
778 : cleanup:
779 : /*
780 : * pre: d0 and d1 must either be NULL or point to fully constructed
781 : * NaClDesc objects
782 : */
783 0 : if (NULL != d0) {
784 0 : NaClDescUnref((struct NaClDesc *) d0);
785 : }
786 0 : if (NULL != d1) {
787 0 : NaClDescUnref((struct NaClDesc *) d1);
788 : }
789 0 : if (NACL_INVALID_HANDLE != sock_pair[0]) {
790 0 : (void) NaClClose(sock_pair[0]);
791 : }
792 0 : if (NACL_INVALID_HANDLE != sock_pair[1]) {
793 0 : (void) NaClClose(sock_pair[1]);
794 : }
795 :
796 0 : free(d0);
797 0 : free(d1);
798 :
799 0 : return retval;
800 0 : }
|