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.
7 #include "native_client/src/trusted/service_runtime/sys_imc.h"
11 #include "native_client/src/trusted/desc/nacl_desc_imc.h"
12 #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
13 #include "native_client/src/trusted/desc/nacl_desc_invalid.h"
14 #include "native_client/src/trusted/desc/nrd_xfer.h"
15 #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
16 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
17 #include "native_client/src/trusted/service_runtime/nacl_copy.h"
18 #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
19 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
22 static int const kKnownInvalidDescNumber = -1;
24 int32_t NaClSysImcMakeBoundSock(struct NaClAppThread *natp,
25 uint32_t descs_addr) {
27 * Create a bound socket descriptor and a socket address descriptor.
29 struct NaClApp *nap = natp->nap;
30 int32_t retval = -NACL_ABI_EINVAL;
31 struct NaClDesc *pair[2];
34 /* This syscall is not used in Chromium so is disabled by default. */
35 if (!NaClAclBypassChecks) {
36 return -NACL_ABI_EACCES;
40 ("Entered NaClSysImcMakeBoundSock(0x%08"NACL_PRIxPTR","
41 " 0x%08"NACL_PRIx32")\n"),
42 (uintptr_t) natp, descs_addr);
44 retval = NaClCommonDescMakeBoundSock(pair);
49 usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]);
50 usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]);
51 if (!NaClCopyOutToUser(nap, descs_addr, usr_pair, sizeof usr_pair)) {
53 * NB: The descriptors were briefly observable to untrusted code
54 * in this window, even though the syscall had not returned yet,
55 * and another thread which guesses their numbers could actually
56 * use them, so the NaClDescSafeUnref inside NaClAppSetDesc below
57 * might not actually deallocate right away. To avoid this, we
58 * could grab the descriptor lock and hold it until after the
59 * copyout is done, but that imposes an ordering between the
60 * descriptor lock and the VM lock which can cause problems
63 NaClAppSetDesc(nap, usr_pair[0], NULL);
64 NaClAppSetDesc(nap, usr_pair[1], NULL);
65 retval = -NACL_ABI_EFAULT;
75 int32_t NaClSysImcAccept(struct NaClAppThread *natp,
77 struct NaClApp *nap = natp->nap;
78 int32_t retval = -NACL_ABI_EINVAL;
81 NaClLog(3, "Entered NaClSysImcAccept(0x%08"NACL_PRIxPTR", %d)\n",
84 ndp = NaClAppGetDesc(nap, d);
86 retval = -NACL_ABI_EBADF;
88 struct NaClDesc *result_desc;
89 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
90 AcceptConn)(ndp, &result_desc);
92 retval = NaClAppSetDescAvail(nap, result_desc);
100 int32_t NaClSysImcConnect(struct NaClAppThread *natp,
102 struct NaClApp *nap = natp->nap;
103 int32_t retval = -NACL_ABI_EINVAL;
104 struct NaClDesc *ndp;
106 NaClLog(3, "Entered NaClSysImcConnectAddr(0x%08"NACL_PRIxPTR", %d)\n",
107 (uintptr_t) natp, d);
109 ndp = NaClAppGetDesc(nap, d);
111 retval = -NACL_ABI_EBADF;
113 struct NaClDesc *result;
114 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
115 ConnectAddr)(ndp, &result);
117 retval = NaClAppSetDescAvail(nap, result);
126 * This function converts addresses from user addresses to system
127 * addresses, copying into kernel space as needed to avoid TOCvTOU
128 * races, then invokes the descriptor's SendMsg() method.
130 int32_t NaClSysImcSendmsg(struct NaClAppThread *natp,
134 struct NaClApp *nap = natp->nap;
135 int32_t retval = -NACL_ABI_EINVAL;
136 ssize_t ssize_retval;
138 /* copy of user-space data for validation */
139 struct NaClAbiNaClImcMsgHdr kern_nanimh;
140 struct NaClAbiNaClImcMsgIoVec kern_naiov[NACL_ABI_IMC_IOVEC_MAX];
141 struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX];
142 int32_t usr_desc[NACL_ABI_IMC_USER_DESC_MAX];
143 /* kernel-side representatin of descriptors */
144 struct NaClDesc *kern_desc[NACL_ABI_IMC_USER_DESC_MAX];
145 struct NaClImcTypedMsgHdr kern_msg_hdr;
146 struct NaClDesc *ndp;
150 ("Entered NaClSysImcSendmsg(0x%08"NACL_PRIxPTR", %d,"
151 " 0x%08"NACL_PRIx32", 0x%x)\n"),
152 (uintptr_t) natp, d, nanimhp, flags);
154 if (!NaClCopyInFromUser(nap, &kern_nanimh, nanimhp, sizeof kern_nanimh)) {
155 NaClLog(4, "NaClImcMsgHdr not in user address space\n");
156 retval = -NACL_ABI_EFAULT;
159 /* copy before validating contents */
162 * Some of these checks duplicate checks that will be done in the
163 * nrd xfer library, but it is better to check before doing the
164 * address translation of memory/descriptor vectors if those vectors
165 * might be too long. Plus, we need to copy and validate vectors
166 * for TOCvTOU race protection, and we must prevent overflows. The
167 * nrd xfer library's checks should never fire when called from the
168 * service runtime, but the nrd xfer library might be called from
171 if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) {
172 NaClLog(4, "gather/scatter array too large\n");
173 retval = -NACL_ABI_EINVAL;
176 if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) {
177 NaClLog(4, "handle vector too long\n");
178 retval = -NACL_ABI_EINVAL;
182 if (kern_nanimh.iov_length > 0) {
183 if (!NaClCopyInFromUser(nap, kern_naiov, (uintptr_t) kern_nanimh.iov,
184 (kern_nanimh.iov_length * sizeof kern_naiov[0]))) {
185 NaClLog(4, "gather/scatter array not in user address space\n");
186 retval = -NACL_ABI_EFAULT;
190 for (i = 0; i < kern_nanimh.iov_length; ++i) {
191 sysaddr = NaClUserToSysAddrRange(nap,
192 (uintptr_t) kern_naiov[i].base,
193 kern_naiov[i].length);
194 if (kNaClBadAddress == sysaddr) {
195 retval = -NACL_ABI_EFAULT;
198 kern_iov[i].base = (void *) sysaddr;
199 kern_iov[i].length = kern_naiov[i].length;
203 ndp = NaClAppGetDesc(nap, d);
205 retval = -NACL_ABI_EBADF;
210 * make things easier for cleaup exit processing
212 memset(kern_desc, 0, sizeof kern_desc);
213 retval = -NACL_ABI_EINVAL;
215 kern_msg_hdr.iov = kern_iov;
216 kern_msg_hdr.iov_length = kern_nanimh.iov_length;
218 if (0 == kern_nanimh.desc_length) {
219 kern_msg_hdr.ndescv = 0;
220 kern_msg_hdr.ndesc_length = 0;
222 if (!NaClCopyInFromUser(nap, usr_desc, kern_nanimh.descv,
223 kern_nanimh.desc_length * sizeof usr_desc[0])) {
224 retval = -NACL_ABI_EFAULT;
228 for (i = 0; i < kern_nanimh.desc_length; ++i) {
229 if (kKnownInvalidDescNumber == usr_desc[i]) {
230 kern_desc[i] = (struct NaClDesc *) NaClDescInvalidMake();
232 /* NaCl modules are ILP32, so this works on ILP32 and LP64 systems */
233 kern_desc[i] = NaClAppGetDesc(nap, usr_desc[i]);
235 if (NULL == kern_desc[i]) {
236 retval = -NACL_ABI_EBADF;
240 kern_msg_hdr.ndescv = kern_desc;
241 kern_msg_hdr.ndesc_length = kern_nanimh.desc_length;
243 kern_msg_hdr.flags = kern_nanimh.flags;
245 /* lock user memory ranges in kern_naiov */
246 for (i = 0; i < kern_nanimh.iov_length; ++i) {
247 NaClVmIoWillStart(nap,
249 kern_naiov[i].base + kern_naiov[i].length - 1);
251 ssize_retval = NACL_VTBL(NaClDesc, ndp)->SendMsg(ndp, &kern_msg_hdr, flags);
252 /* unlock user memory ranges in kern_naiov */
253 for (i = 0; i < kern_nanimh.iov_length; ++i) {
254 NaClVmIoHasEnded(nap,
256 kern_naiov[i].base + kern_naiov[i].length - 1);
259 if (NaClSSizeIsNegErrno(&ssize_retval)) {
261 * NaClWouldBlock uses TSD (for both the errno-based and
262 * GetLastError()-based implementations), so this is threadsafe.
264 if (0 != (flags & NACL_DONT_WAIT) && NaClWouldBlock()) {
265 retval = -NACL_ABI_EAGAIN;
266 } else if (-NACL_ABI_EMSGSIZE == ssize_retval) {
268 * Allow the caller to handle the case when imc_sendmsg fails because
269 * the message is too large for the system to send in one piece.
271 retval = -NACL_ABI_EMSGSIZE;
274 * TODO(bsy): the else case is some mysterious internal error.
275 * Should we destroy the ndp or otherwise mark it as bad? Was
276 * the failure atomic? Did it send some partial data? Linux
277 * implementation appears okay.
279 retval = -NACL_ABI_EIO;
281 } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) {
282 retval = -NACL_ABI_EOVERFLOW;
284 /* cast is safe due to range checks above */
285 retval = (int32_t)ssize_retval;
289 for (i = 0; i < kern_nanimh.desc_length; ++i) {
290 if (NULL != kern_desc[i]) {
291 NaClDescUnref(kern_desc[i]);
297 NaClLog(3, "NaClSysImcSendmsg: returning %d\n", retval);
301 int32_t NaClSysImcRecvmsg(struct NaClAppThread *natp,
305 struct NaClApp *nap = natp->nap;
306 int32_t retval = -NACL_ABI_EINVAL;
307 ssize_t ssize_retval;
310 struct NaClDesc *ndp;
311 struct NaClAbiNaClImcMsgHdr kern_nanimh;
312 struct NaClAbiNaClImcMsgIoVec kern_naiov[NACL_ABI_IMC_IOVEC_MAX];
313 struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX];
314 int32_t usr_desc[NACL_ABI_IMC_USER_DESC_MAX];
315 struct NaClImcTypedMsgHdr recv_hdr;
316 struct NaClDesc *new_desc[NACL_ABI_IMC_DESC_MAX];
317 nacl_abi_size_t num_user_desc;
318 struct NaClDesc *invalid_desc = NULL;
321 ("Entered NaClSysImcRecvMsg(0x%08"NACL_PRIxPTR", %d,"
322 " 0x%08"NACL_PRIx32")\n"),
323 (uintptr_t) natp, d, nanimhp);
326 * First, we validate user-supplied message headers before
327 * allocating a receive buffer.
329 if (!NaClCopyInFromUser(nap, &kern_nanimh, nanimhp, sizeof kern_nanimh)) {
330 NaClLog(4, "NaClImcMsgHdr not in user address space\n");
331 retval = -NACL_ABI_EFAULT;
334 /* copy before validating */
336 if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) {
337 NaClLog(4, "gather/scatter array too large: %"NACL_PRIdNACL_SIZE"\n",
338 kern_nanimh.iov_length);
339 retval = -NACL_ABI_EINVAL;
342 if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) {
343 NaClLog(4, "handle vector too long: %"NACL_PRIdNACL_SIZE"\n",
344 kern_nanimh.desc_length);
345 retval = -NACL_ABI_EINVAL;
349 if (kern_nanimh.iov_length > 0) {
351 * Copy IOV array into kernel space. Validate this snapshot and do
352 * user->kernel address conversions on this snapshot.
354 if (!NaClCopyInFromUser(nap, kern_naiov, (uintptr_t) kern_nanimh.iov,
355 (kern_nanimh.iov_length * sizeof kern_naiov[0]))) {
356 NaClLog(4, "gather/scatter array not in user address space\n");
357 retval = -NACL_ABI_EFAULT;
361 * Convert every IOV base from user to system address, validate
362 * range of bytes are really in user address space.
365 for (i = 0; i < kern_nanimh.iov_length; ++i) {
366 sysaddr = NaClUserToSysAddrRange(nap,
367 (uintptr_t) kern_naiov[i].base,
368 kern_naiov[i].length);
369 if (kNaClBadAddress == sysaddr) {
370 NaClLog(4, "iov number %"NACL_PRIuS" not entirely in user space\n", i);
371 retval = -NACL_ABI_EFAULT;
374 kern_iov[i].base = (void *) sysaddr;
375 kern_iov[i].length = kern_naiov[i].length;
379 if (kern_nanimh.desc_length > 0) {
380 sysaddr = NaClUserToSysAddrRange(nap,
381 (uintptr_t) kern_nanimh.descv,
382 kern_nanimh.desc_length * sizeof(int32_t));
383 if (kNaClBadAddress == sysaddr) {
384 retval = -NACL_ABI_EFAULT;
389 ndp = NaClAppGetDesc(nap, d);
391 NaClLog(4, "receiving descriptor invalid\n");
392 retval = -NACL_ABI_EBADF;
396 recv_hdr.iov = kern_iov;
397 recv_hdr.iov_length = kern_nanimh.iov_length;
399 recv_hdr.ndescv = new_desc;
400 recv_hdr.ndesc_length = NACL_ARRAY_SIZE(new_desc);
401 memset(new_desc, 0, sizeof new_desc);
403 recv_hdr.flags = 0; /* just to make it obvious; IMC will clear it for us */
405 /* lock user memory ranges in kern_naiov */
406 for (i = 0; i < kern_nanimh.iov_length; ++i) {
407 NaClVmIoWillStart(nap,
409 kern_naiov[i].base + kern_naiov[i].length - 1);
411 ssize_retval = NACL_VTBL(NaClDesc, ndp)->RecvMsg(ndp, &recv_hdr, flags,
412 (struct NaClDescQuotaInterface *) nap->desc_quota_interface);
413 /* unlock user memory ranges in kern_naiov */
414 for (i = 0; i < kern_nanimh.iov_length; ++i) {
415 NaClVmIoHasEnded(nap,
417 kern_naiov[i].base + kern_naiov[i].length - 1);
420 * retval is number of user payload bytes received and excludes the
423 NaClLog(3, "NaClSysImcRecvMsg: RecvMsg() returned %"NACL_PRIdS"\n",
425 if (NaClSSizeIsNegErrno(&ssize_retval)) {
426 /* negative error numbers all have valid 32-bit representations,
427 * so this cast is safe. */
428 retval = (int32_t) ssize_retval;
430 } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) {
431 retval = -NACL_ABI_EOVERFLOW;
434 /* cast is safe due to range check above */
435 retval = (int32_t) ssize_retval;
439 * NB: recv_hdr.flags may contain NACL_ABI_MESSAGE_TRUNCATED and/or
440 * NACL_ABI_HANDLES_TRUNCATED.
443 kern_nanimh.flags = recv_hdr.flags;
446 * Now internalize the NaClHandles as NaClDesc objects.
448 num_user_desc = recv_hdr.ndesc_length;
450 if (kern_nanimh.desc_length < num_user_desc) {
451 kern_nanimh.flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED;
452 for (i = kern_nanimh.desc_length; i < num_user_desc; ++i) {
453 NaClDescUnref(new_desc[i]);
456 num_user_desc = kern_nanimh.desc_length;
459 invalid_desc = (struct NaClDesc *) NaClDescInvalidMake();
460 /* prepare to write out to user space the descriptor numbers */
461 for (i = 0; i < num_user_desc; ++i) {
462 if (invalid_desc == new_desc[i]) {
463 usr_desc[i] = kKnownInvalidDescNumber;
464 NaClDescUnref(new_desc[i]);
466 usr_desc[i] = NaClAppSetDescAvail(nap, new_desc[i]);
470 if (0 != num_user_desc &&
471 !NaClCopyOutToUser(nap, (uintptr_t) kern_nanimh.descv, usr_desc,
472 num_user_desc * sizeof usr_desc[0])) {
474 ("NaClSysImcRecvMsg: in/out ptr (descv %"NACL_PRIxPTR
475 ") became invalid at copyout?\n"),
476 (uintptr_t) kern_nanimh.descv);
479 kern_nanimh.desc_length = num_user_desc;
480 if (!NaClCopyOutToUser(nap, nanimhp, &kern_nanimh, sizeof kern_nanimh)) {
482 "NaClSysImcRecvMsg: in/out ptr (iov) became"
483 " invalid at copyout?\n");
485 /* copy out updated desc count, flags */
488 for (i = 0; i < NACL_ARRAY_SIZE(new_desc); ++i) {
489 if (NULL != new_desc[i]) {
490 NaClDescUnref(new_desc[i]);
496 NaClDescSafeUnref(invalid_desc);
497 NaClLog(3, "NaClSysImcRecvMsg: returning %d\n", retval);
502 int32_t NaClSysImcMemObjCreate(struct NaClAppThread *natp,
504 struct NaClApp *nap = natp->nap;
505 int32_t retval = -NACL_ABI_EINVAL;
506 struct NaClDescImcShm *shmp;
510 ("Entered NaClSysImcMemObjCreate(0x%08"NACL_PRIxPTR
511 " 0x%08"NACL_PRIxS")\n"),
512 (uintptr_t) natp, size);
514 /* This syscall is not used in Chromium so is disabled by default. */
515 if (!NaClAclBypassChecks) {
516 return -NACL_ABI_EACCES;
519 if (0 != (size & (NACL_MAP_PAGESIZE - 1))) {
520 return -NACL_ABI_EINVAL;
523 * TODO(bsy): policy about maximum shm object size should be
526 size_as_off = (off_t) size;
527 if (size_as_off < 0) {
528 return -NACL_ABI_EINVAL;
533 shmp = malloc(sizeof *shmp);
535 retval = -NACL_ABI_ENOMEM;
539 if (!NaClDescImcShmAllocCtor(shmp, size_as_off, /* executable= */ 0)) {
540 retval = -NACL_ABI_ENOMEM; /* is this reasonable? */
544 retval = NaClAppSetDescAvail(nap, (struct NaClDesc *) shmp);
553 int32_t NaClSysImcSocketPair(struct NaClAppThread *natp,
554 uint32_t descs_out) {
555 struct NaClApp *nap = natp->nap;
557 struct NaClDesc *pair[2];
561 ("Entered NaClSysImcSocketPair(0x%08"NACL_PRIxPTR
562 " 0x%08"NACL_PRIx32")\n"),
563 (uintptr_t) natp, descs_out);
565 /* This syscall is not used in Chromium so is disabled by default. */
566 if (!NaClAclBypassChecks) {
567 return -NACL_ABI_EACCES;
570 retval = NaClCommonDescSocketPair(pair);
575 usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]);
576 usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]);
578 if (!NaClCopyOutToUser(nap, (uintptr_t) descs_out, usr_pair,
580 NaClAppSetDesc(nap, usr_pair[0], NULL);
581 NaClAppSetDesc(nap, usr_pair[1], NULL);
582 retval = -NACL_ABI_EFAULT;