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/sel_ldr.h"
21 static int const kKnownInvalidDescNumber = -1;
23 int32_t NaClSysImcMakeBoundSock(struct NaClAppThread *natp,
26 * Create a bound socket descriptor and a socket address descriptor.
28 struct NaClApp *nap = natp->nap;
29 int32_t retval = -NACL_ABI_EINVAL;
30 struct NaClDesc *pair[2];
34 ("Entered NaClSysImcMakeBoundSock(0x%08"NACL_PRIxPTR","
35 " 0x%08"NACL_PRIxPTR")\n"),
36 (uintptr_t) natp, (uintptr_t) sap);
38 retval = NaClCommonDescMakeBoundSock(pair);
43 usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]);
44 usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]);
45 if (!NaClCopyOutToUser(nap, (uintptr_t) sap,
46 usr_pair, sizeof usr_pair)) {
48 * NB: The descriptors were briefly observable to untrusted code
49 * in this window, even though the syscall had not returned yet,
50 * and another thread which guesses their numbers could actually
51 * use them, so the NaClDescSafeUnref inside NaClAppSetDesc below
52 * might not actually deallocate right away. To avoid this, we
53 * could grab the descriptor lock and hold it until after the
54 * copyout is done, but that imposes an ordering between the
55 * descriptor lock and the VM lock which can cause problems
58 NaClAppSetDesc(nap, usr_pair[0], NULL);
59 NaClAppSetDesc(nap, usr_pair[1], NULL);
60 retval = -NACL_ABI_EFAULT;
70 int32_t NaClSysImcAccept(struct NaClAppThread *natp,
72 struct NaClApp *nap = natp->nap;
73 int32_t retval = -NACL_ABI_EINVAL;
76 NaClLog(3, "Entered NaClSysImcAccept(0x%08"NACL_PRIxPTR", %d)\n",
79 ndp = NaClAppGetDesc(nap, d);
81 retval = -NACL_ABI_EBADF;
83 struct NaClDesc *result_desc;
84 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
85 AcceptConn)(ndp, &result_desc);
87 retval = NaClAppSetDescAvail(nap, result_desc);
95 int32_t NaClSysImcConnect(struct NaClAppThread *natp,
97 struct NaClApp *nap = natp->nap;
98 int32_t retval = -NACL_ABI_EINVAL;
101 NaClLog(3, "Entered NaClSysImcConnectAddr(0x%08"NACL_PRIxPTR", %d)\n",
102 (uintptr_t) natp, d);
104 ndp = NaClAppGetDesc(nap, d);
106 retval = -NACL_ABI_EBADF;
108 struct NaClDesc *result;
109 retval = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
110 ConnectAddr)(ndp, &result);
112 retval = NaClAppSetDescAvail(nap, result);
121 * This function converts addresses from user addresses to system
122 * addresses, copying into kernel space as needed to avoid TOCvTOU
123 * races, then invokes the descriptor's SendMsg() method.
125 int32_t NaClSysImcSendmsg(struct NaClAppThread *natp,
127 struct NaClAbiNaClImcMsgHdr *nanimhp,
129 struct NaClApp *nap = natp->nap;
130 int32_t retval = -NACL_ABI_EINVAL;
131 ssize_t ssize_retval;
133 /* copy of user-space data for validation */
134 struct NaClAbiNaClImcMsgHdr kern_nanimh;
135 struct NaClAbiNaClImcMsgIoVec kern_naiov[NACL_ABI_IMC_IOVEC_MAX];
136 struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX];
137 int32_t usr_desc[NACL_ABI_IMC_USER_DESC_MAX];
138 /* kernel-side representatin of descriptors */
139 struct NaClDesc *kern_desc[NACL_ABI_IMC_USER_DESC_MAX];
140 struct NaClImcTypedMsgHdr kern_msg_hdr;
141 struct NaClDesc *ndp;
145 ("Entered NaClSysImcSendmsg(0x%08"NACL_PRIxPTR", %d,"
146 " 0x%08"NACL_PRIxPTR", 0x%x)\n"),
147 (uintptr_t) natp, d, (uintptr_t) nanimhp, flags);
149 if (!NaClCopyInFromUser(nap, &kern_nanimh, (uintptr_t) nanimhp,
150 sizeof kern_nanimh)) {
151 NaClLog(4, "NaClImcMsgHdr not in user address space\n");
152 retval = -NACL_ABI_EFAULT;
155 /* copy before validating contents */
158 * Some of these checks duplicate checks that will be done in the
159 * nrd xfer library, but it is better to check before doing the
160 * address translation of memory/descriptor vectors if those vectors
161 * might be too long. Plus, we need to copy and validate vectors
162 * for TOCvTOU race protection, and we must prevent overflows. The
163 * nrd xfer library's checks should never fire when called from the
164 * service runtime, but the nrd xfer library might be called from
167 if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) {
168 NaClLog(4, "gather/scatter array too large\n");
169 retval = -NACL_ABI_EINVAL;
172 if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) {
173 NaClLog(4, "handle vector too long\n");
174 retval = -NACL_ABI_EINVAL;
178 if (kern_nanimh.iov_length > 0) {
179 if (!NaClCopyInFromUser(nap, kern_naiov, (uintptr_t) kern_nanimh.iov,
180 (kern_nanimh.iov_length * sizeof kern_naiov[0]))) {
181 NaClLog(4, "gather/scatter array not in user address space\n");
182 retval = -NACL_ABI_EFAULT;
186 for (i = 0; i < kern_nanimh.iov_length; ++i) {
187 sysaddr = NaClUserToSysAddrRange(nap,
188 (uintptr_t) kern_naiov[i].base,
189 kern_naiov[i].length);
190 if (kNaClBadAddress == sysaddr) {
191 retval = -NACL_ABI_EFAULT;
194 kern_iov[i].base = (void *) sysaddr;
195 kern_iov[i].length = kern_naiov[i].length;
199 ndp = NaClAppGetDesc(nap, d);
201 retval = -NACL_ABI_EBADF;
206 * make things easier for cleaup exit processing
208 memset(kern_desc, 0, sizeof kern_desc);
209 retval = -NACL_ABI_EINVAL;
211 kern_msg_hdr.iov = kern_iov;
212 kern_msg_hdr.iov_length = kern_nanimh.iov_length;
214 if (0 == kern_nanimh.desc_length) {
215 kern_msg_hdr.ndescv = 0;
216 kern_msg_hdr.ndesc_length = 0;
218 if (!NaClCopyInFromUser(nap, usr_desc, kern_nanimh.descv,
219 kern_nanimh.desc_length * sizeof usr_desc[0])) {
220 retval = -NACL_ABI_EFAULT;
224 for (i = 0; i < kern_nanimh.desc_length; ++i) {
225 if (kKnownInvalidDescNumber == usr_desc[i]) {
226 kern_desc[i] = (struct NaClDesc *) NaClDescInvalidMake();
228 /* NaCl modules are ILP32, so this works on ILP32 and LP64 systems */
229 kern_desc[i] = NaClAppGetDesc(nap, usr_desc[i]);
231 if (NULL == kern_desc[i]) {
232 retval = -NACL_ABI_EBADF;
236 kern_msg_hdr.ndescv = kern_desc;
237 kern_msg_hdr.ndesc_length = kern_nanimh.desc_length;
239 kern_msg_hdr.flags = kern_nanimh.flags;
241 /* lock user memory ranges in kern_naiov */
242 for (i = 0; i < kern_nanimh.iov_length; ++i) {
243 NaClVmIoWillStart(nap,
245 kern_naiov[i].base + kern_naiov[i].length - 1);
247 ssize_retval = NACL_VTBL(NaClDesc, ndp)->SendMsg(ndp, &kern_msg_hdr, flags);
248 /* unlock user memory ranges in kern_naiov */
249 for (i = 0; i < kern_nanimh.iov_length; ++i) {
250 NaClVmIoHasEnded(nap,
252 kern_naiov[i].base + kern_naiov[i].length - 1);
255 if (NaClSSizeIsNegErrno(&ssize_retval)) {
257 * NaClWouldBlock uses TSD (for both the errno-based and
258 * GetLastError()-based implementations), so this is threadsafe.
260 if (0 != (flags & NACL_DONT_WAIT) && NaClWouldBlock()) {
261 retval = -NACL_ABI_EAGAIN;
262 } else if (-NACL_ABI_EMSGSIZE == ssize_retval) {
264 * Allow the caller to handle the case when imc_sendmsg fails because
265 * the message is too large for the system to send in one piece.
267 retval = -NACL_ABI_EMSGSIZE;
270 * TODO(bsy): the else case is some mysterious internal error.
271 * Should we destroy the ndp or otherwise mark it as bad? Was
272 * the failure atomic? Did it send some partial data? Linux
273 * implementation appears okay.
275 retval = -NACL_ABI_EIO;
277 } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) {
278 retval = -NACL_ABI_EOVERFLOW;
280 /* cast is safe due to range checks above */
281 retval = (int32_t)ssize_retval;
285 for (i = 0; i < kern_nanimh.desc_length; ++i) {
286 if (NULL != kern_desc[i]) {
287 NaClDescUnref(kern_desc[i]);
293 NaClLog(3, "NaClSysImcSendmsg: returning %d\n", retval);
297 int32_t NaClSysImcRecvmsg(struct NaClAppThread *natp,
299 struct NaClAbiNaClImcMsgHdr *nanimhp,
301 struct NaClApp *nap = natp->nap;
302 int32_t retval = -NACL_ABI_EINVAL;
303 ssize_t ssize_retval;
306 struct NaClDesc *ndp;
307 struct NaClAbiNaClImcMsgHdr kern_nanimh;
308 struct NaClAbiNaClImcMsgIoVec kern_naiov[NACL_ABI_IMC_IOVEC_MAX];
309 struct NaClImcMsgIoVec kern_iov[NACL_ABI_IMC_IOVEC_MAX];
310 int32_t usr_desc[NACL_ABI_IMC_USER_DESC_MAX];
311 struct NaClImcTypedMsgHdr recv_hdr;
312 struct NaClDesc *new_desc[NACL_ABI_IMC_DESC_MAX];
313 nacl_abi_size_t num_user_desc;
314 struct NaClDesc *invalid_desc = NULL;
317 ("Entered NaClSysImcRecvMsg(0x%08"NACL_PRIxPTR", %d,"
318 " 0x%08"NACL_PRIxPTR")\n"),
319 (uintptr_t) natp, d, (uintptr_t) nanimhp);
322 * First, we validate user-supplied message headers before
323 * allocating a receive buffer.
325 if (!NaClCopyInFromUser(nap, &kern_nanimh, (uintptr_t) nanimhp,
326 sizeof kern_nanimh)) {
327 NaClLog(4, "NaClImcMsgHdr not in user address space\n");
328 retval = -NACL_ABI_EFAULT;
331 /* copy before validating */
333 if (kern_nanimh.iov_length > NACL_ABI_IMC_IOVEC_MAX) {
334 NaClLog(4, "gather/scatter array too large: %"NACL_PRIdNACL_SIZE"\n",
335 kern_nanimh.iov_length);
336 retval = -NACL_ABI_EINVAL;
339 if (kern_nanimh.desc_length > NACL_ABI_IMC_USER_DESC_MAX) {
340 NaClLog(4, "handle vector too long: %"NACL_PRIdNACL_SIZE"\n",
341 kern_nanimh.desc_length);
342 retval = -NACL_ABI_EINVAL;
346 if (kern_nanimh.iov_length > 0) {
348 * Copy IOV array into kernel space. Validate this snapshot and do
349 * user->kernel address conversions on this snapshot.
351 if (!NaClCopyInFromUser(nap, kern_naiov, (uintptr_t) kern_nanimh.iov,
352 (kern_nanimh.iov_length * sizeof kern_naiov[0]))) {
353 NaClLog(4, "gather/scatter array not in user address space\n");
354 retval = -NACL_ABI_EFAULT;
358 * Convert every IOV base from user to system address, validate
359 * range of bytes are really in user address space.
362 for (i = 0; i < kern_nanimh.iov_length; ++i) {
363 sysaddr = NaClUserToSysAddrRange(nap,
364 (uintptr_t) kern_naiov[i].base,
365 kern_naiov[i].length);
366 if (kNaClBadAddress == sysaddr) {
367 NaClLog(4, "iov number %"NACL_PRIdS" not entirely in user space\n", i);
368 retval = -NACL_ABI_EFAULT;
371 kern_iov[i].base = (void *) sysaddr;
372 kern_iov[i].length = kern_naiov[i].length;
376 if (kern_nanimh.desc_length > 0) {
377 sysaddr = NaClUserToSysAddrRange(nap,
378 (uintptr_t) kern_nanimh.descv,
379 kern_nanimh.desc_length * sizeof(int32_t));
380 if (kNaClBadAddress == sysaddr) {
381 retval = -NACL_ABI_EFAULT;
386 ndp = NaClAppGetDesc(nap, d);
388 NaClLog(4, "receiving descriptor invalid\n");
389 retval = -NACL_ABI_EBADF;
393 recv_hdr.iov = kern_iov;
394 recv_hdr.iov_length = kern_nanimh.iov_length;
396 recv_hdr.ndescv = new_desc;
397 recv_hdr.ndesc_length = NACL_ARRAY_SIZE(new_desc);
398 memset(new_desc, 0, sizeof new_desc);
400 recv_hdr.flags = 0; /* just to make it obvious; IMC will clear it for us */
402 /* lock user memory ranges in kern_naiov */
403 for (i = 0; i < kern_nanimh.iov_length; ++i) {
404 NaClVmIoWillStart(nap,
406 kern_naiov[i].base + kern_naiov[i].length - 1);
408 ssize_retval = NACL_VTBL(NaClDesc, ndp)->RecvMsg(ndp, &recv_hdr, flags,
409 (struct NaClDescQuotaInterface *) nap->desc_quota_interface);
410 /* unlock user memory ranges in kern_naiov */
411 for (i = 0; i < kern_nanimh.iov_length; ++i) {
412 NaClVmIoHasEnded(nap,
414 kern_naiov[i].base + kern_naiov[i].length - 1);
417 * retval is number of user payload bytes received and excludes the
420 NaClLog(3, "NaClSysImcRecvMsg: RecvMsg() returned %"NACL_PRIdS"\n",
422 if (NaClSSizeIsNegErrno(&ssize_retval)) {
423 /* negative error numbers all have valid 32-bit representations,
424 * so this cast is safe. */
425 retval = (int32_t) ssize_retval;
427 } else if (ssize_retval > INT32_MAX || ssize_retval < INT32_MIN) {
428 retval = -NACL_ABI_EOVERFLOW;
431 /* cast is safe due to range check above */
432 retval = (int32_t) ssize_retval;
436 * NB: recv_hdr.flags may contain NACL_ABI_MESSAGE_TRUNCATED and/or
437 * NACL_ABI_HANDLES_TRUNCATED.
440 kern_nanimh.flags = recv_hdr.flags;
443 * Now internalize the NaClHandles as NaClDesc objects.
445 num_user_desc = recv_hdr.ndesc_length;
447 if (kern_nanimh.desc_length < num_user_desc) {
448 kern_nanimh.flags |= NACL_ABI_RECVMSG_DESC_TRUNCATED;
449 for (i = kern_nanimh.desc_length; i < num_user_desc; ++i) {
450 NaClDescUnref(new_desc[i]);
453 num_user_desc = kern_nanimh.desc_length;
456 invalid_desc = (struct NaClDesc *) NaClDescInvalidMake();
457 /* prepare to write out to user space the descriptor numbers */
458 for (i = 0; i < num_user_desc; ++i) {
459 if (invalid_desc == new_desc[i]) {
460 usr_desc[i] = kKnownInvalidDescNumber;
461 NaClDescUnref(new_desc[i]);
463 usr_desc[i] = NaClAppSetDescAvail(nap, new_desc[i]);
467 if (0 != num_user_desc &&
468 !NaClCopyOutToUser(nap, (uintptr_t) kern_nanimh.descv, usr_desc,
469 num_user_desc * sizeof usr_desc[0])) {
471 ("NaClSysImcRecvMsg: in/out ptr (descv %"NACL_PRIxPTR
472 ") became invalid at copyout?\n"),
473 (uintptr_t) kern_nanimh.descv);
476 kern_nanimh.desc_length = num_user_desc;
477 if (!NaClCopyOutToUser(nap, (uintptr_t) nanimhp, &kern_nanimh,
478 sizeof kern_nanimh)) {
480 "NaClSysImcRecvMsg: in/out ptr (iov) became"
481 " invalid at copyout?\n");
483 /* copy out updated desc count, flags */
486 for (i = 0; i < NACL_ARRAY_SIZE(new_desc); ++i) {
487 if (NULL != new_desc[i]) {
488 NaClDescUnref(new_desc[i]);
494 NaClDescSafeUnref(invalid_desc);
495 NaClLog(3, "NaClSysImcRecvMsg: returning %d\n", retval);
500 int32_t NaClSysImcMemObjCreate(struct NaClAppThread *natp,
502 struct NaClApp *nap = natp->nap;
503 int32_t retval = -NACL_ABI_EINVAL;
504 struct NaClDescImcShm *shmp;
508 ("Entered NaClSysImcMemObjCreate(0x%08"NACL_PRIxPTR
509 " 0x%08"NACL_PRIxS")\n"),
510 (uintptr_t) natp, size);
512 if (0 != (size & (NACL_MAP_PAGESIZE - 1))) {
513 return -NACL_ABI_EINVAL;
516 * TODO(bsy): policy about maximum shm object size should be
519 size_as_off = (off_t) size;
520 if (size_as_off < 0) {
521 return -NACL_ABI_EINVAL;
526 shmp = malloc(sizeof *shmp);
528 retval = -NACL_ABI_ENOMEM;
532 if (!NaClDescImcShmAllocCtor(shmp, size_as_off, /* executable= */ 0)) {
533 retval = -NACL_ABI_ENOMEM; /* is this reasonable? */
537 retval = NaClAppSetDescAvail(nap, (struct NaClDesc *) shmp);
546 int32_t NaClSysImcSocketPair(struct NaClAppThread *natp,
547 uint32_t descs_out) {
548 struct NaClApp *nap = natp->nap;
550 struct NaClDesc *pair[2];
554 ("Entered NaClSysImcSocketPair(0x%08"NACL_PRIxPTR
555 " 0x%08"NACL_PRIx32")\n"),
556 (uintptr_t) natp, descs_out);
558 retval = NaClCommonDescSocketPair(pair);
563 usr_pair[0] = NaClAppSetDescAvail(nap, pair[0]);
564 usr_pair[1] = NaClAppSetDescAvail(nap, pair[1]);
566 if (!NaClCopyOutToUser(nap, (uintptr_t) descs_out, usr_pair,
568 NaClAppSetDesc(nap, usr_pair[0], NULL);
569 NaClAppSetDesc(nap, usr_pair[1], NULL);
570 retval = -NACL_ABI_EFAULT;