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