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 : * NaCl Service Runtime. I/O Descriptor / Handle abstraction.
9 : */
10 :
11 : #include <stdlib.h>
12 : #include <string.h>
13 : #include <errno.h>
14 :
15 : #include "native_client/src/include/portability.h"
16 :
17 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
18 : #include "native_client/src/trusted/desc/nacl_desc_imc.h"
19 : #include "native_client/src/trusted/desc/nrd_xfer.h"
20 :
21 : #include "native_client/src/shared/platform/nacl_log.h"
22 : #include "native_client/src/shared/platform/nacl_sync.h"
23 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
24 :
25 : #if NACL_WINDOWS
26 : # include "native_client/src/shared/platform/win/xlate_system_error.h"
27 : #endif
28 :
29 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
30 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
31 :
32 : /*
33 : * This file contains the implementation of the NaClDescImcDesc
34 : * subclass of NaClDesc.
35 : *
36 : * NaClDescImcDesc is the subclass that wraps IMC socket descriptors.
37 : */
38 :
39 : /* fwd */
40 : static struct NaClDescVtbl const kNaClDescImcConnectedDescVtbl;
41 : static struct NaClDescVtbl const kNaClDescImcDescVtbl;
42 : static struct NaClDescVtbl const kNaClDescXferableDataDescVtbl;
43 :
44 : static int NaClDescImcConnectedDescSubclassCtor(
45 : struct NaClDescImcConnectedDesc *self,
46 4 : NaClHandle h) {
47 4 : struct NaClDesc *basep = (struct NaClDesc *) self;
48 :
49 4 : self->h = h;
50 : basep->base.vtbl = (struct NaClRefCountVtbl const *)
51 4 : &kNaClDescImcConnectedDescVtbl;
52 :
53 4 : return 1;
54 4 : }
55 :
56 : int NaClDescImcConnectedDescCtor(struct NaClDescImcConnectedDesc *self,
57 4 : NaClHandle h) {
58 4 : struct NaClDesc *basep = (struct NaClDesc *) self;
59 : int rv;
60 :
61 4 : basep->base.vtbl = (struct NaClRefCountVtbl const *) NULL;
62 :
63 4 : if (!NaClDescCtor(basep)) {
64 0 : return 0;
65 : }
66 4 : rv = NaClDescImcConnectedDescSubclassCtor(self, h);
67 4 : if (!rv) {
68 0 : (*NACL_VTBL(NaClRefCount, basep)->Dtor)((struct NaClRefCount *) basep);
69 : }
70 :
71 4 : return rv;
72 4 : }
73 :
74 2 : static void NaClDescImcConnectedDescDtor(struct NaClRefCount *vself) {
75 : struct NaClDescImcConnectedDesc *self = ((struct NaClDescImcConnectedDesc *)
76 2 : vself);
77 2 : if (self->h != NACL_INVALID_HANDLE) {
78 2 : (void) NaClClose(self->h);
79 : }
80 2 : self->h = NACL_INVALID_HANDLE;
81 2 : vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl;
82 2 : (*vself->vtbl->Dtor)(vself);
83 2 : }
84 :
85 : int NaClDescImcDescCtor(struct NaClDescImcDesc *self,
86 4 : NaClHandle h) {
87 : int retval;
88 :
89 4 : retval = NaClDescImcConnectedDescCtor(&self->base, h);
90 4 : if (!retval) {
91 0 : return 0;
92 : }
93 4 : if (!NaClMutexCtor(&self->sendmsg_mu)) {
94 0 : NaClDescUnref((struct NaClDesc *) self);
95 0 : return 0;
96 : }
97 4 : if (!NaClMutexCtor(&self->recvmsg_mu)) {
98 0 : NaClMutexDtor(&self->sendmsg_mu);
99 0 : NaClDescUnref((struct NaClDesc *) self);
100 0 : return 0;
101 : }
102 : self->base.base.base.vtbl = (struct NaClRefCountVtbl const *)
103 4 : &kNaClDescImcDescVtbl;
104 :
105 4 : return retval;
106 4 : }
107 :
108 2 : static void NaClDescImcDescDtor(struct NaClRefCount *vself) {
109 : struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *)
110 2 : vself);
111 2 : NaClMutexDtor(&self->sendmsg_mu);
112 2 : NaClMutexDtor(&self->recvmsg_mu);
113 :
114 : vself->vtbl = (struct NaClRefCountVtbl const *)
115 2 : &kNaClDescImcConnectedDescVtbl;
116 2 : (*vself->vtbl->Dtor)(vself);
117 2 : }
118 :
119 : /*
120 : * Construct NaclDescImcConnectedDesc then NaClDescXferableDataDesc,
121 : * assuming NaClDesc construction in |*self| has already occurred.
122 : */
123 : static int NaClDescXferableDataDescSubclassesCtor(
124 : struct NaClDescXferableDataDesc *self,
125 0 : NaClHandle h) {
126 : int retval;
127 :
128 0 : retval = NaClDescImcConnectedDescSubclassCtor(&self->base, h);
129 0 : if (!retval) {
130 0 : return 0;
131 : }
132 : self->base.base.base.vtbl = (struct NaClRefCountVtbl const *)
133 0 : &kNaClDescXferableDataDescVtbl;
134 :
135 0 : return retval;
136 0 : }
137 :
138 : int NaClDescXferableDataDescCtor(struct NaClDescXferableDataDesc *self,
139 0 : NaClHandle h) {
140 : int retval;
141 :
142 0 : retval = NaClDescImcConnectedDescCtor(&self->base, h);
143 0 : if (!retval) {
144 0 : return 0;
145 : }
146 : self->base.base.base.vtbl = (struct NaClRefCountVtbl const *)
147 0 : &kNaClDescXferableDataDescVtbl;
148 :
149 0 : return retval;
150 0 : }
151 :
152 0 : static void NaClDescXferableDataDescDtor(struct NaClRefCount *vself) {
153 : vself->vtbl = (struct NaClRefCountVtbl const *)
154 0 : &kNaClDescImcConnectedDescVtbl;
155 0 : (*vself->vtbl->Dtor)(vself);
156 0 : }
157 :
158 : int NaClDescImcDescFstat(struct NaClDesc *vself,
159 0 : struct nacl_abi_stat *statbuf) {
160 : UNREFERENCED_PARAMETER(vself);
161 :
162 0 : memset(statbuf, 0, sizeof *statbuf);
163 : statbuf->nacl_abi_st_mode = (NACL_ABI_S_IFSOCK |
164 : NACL_ABI_S_IRUSR |
165 0 : NACL_ABI_S_IWUSR);
166 0 : return 0;
167 0 : }
168 :
169 : static int NaClDescXferableDataDescFstat(struct NaClDesc *vself,
170 0 : struct nacl_abi_stat *statbuf) {
171 : UNREFERENCED_PARAMETER(vself);
172 :
173 0 : memset(statbuf, 0, sizeof *statbuf);
174 0 : statbuf->nacl_abi_st_mode = NACL_ABI_S_IFDSOCK;
175 0 : return 0;
176 0 : }
177 :
178 : static int NaClDescXferableDataDescExternalizeSize(struct NaClDesc *vself,
179 : size_t *nbytes,
180 0 : size_t *nhandles) {
181 : int rv;
182 :
183 : UNREFERENCED_PARAMETER(vself);
184 0 : NaClLog(4, "Entered NaClDescXferableDataDescExternalizeSize\n");
185 0 : rv = NaClDescExternalizeSize(vself, nbytes, nhandles);
186 0 : if (0 != rv) {
187 0 : return rv;
188 : }
189 0 : *nhandles += 1;
190 0 : return 0;
191 0 : }
192 :
193 : static int NaClDescXferableDataDescExternalize(struct NaClDesc *vself,
194 0 : struct NaClDescXferState *xfer) {
195 : struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *)
196 0 : vself);
197 : int rv;
198 :
199 0 : NaClLog(4, "Entered NaClDescXferableDataDescExternalize\n");
200 0 : rv = NaClDescExternalize(vself, xfer);
201 0 : if (0 != rv) {
202 0 : return rv;
203 : }
204 0 : *xfer->next_handle++ = self->base.h;
205 0 : return 0;
206 0 : }
207 :
208 :
209 : /*
210 : * In the level of NaClDescImcDescLowLevelSendMsg, we do not know what
211 : * protocol is implemented by NaClSendDatagram (and indeed, in the
212 : * Windows implementation, the access rights transfer involves a more
213 : * complex protocol to get the peer process id). Because the
214 : * underlying low-level IMC implementation does not provide thread
215 : * safety, this is the source of race conditions: two simultaneous
216 : * imc_sendmsg syscalls to the same descriptor could get their various
217 : * protcol bits interleaved, so the receiver will get garbled data
218 : * that cause the wrong underlying host OS descriptor to be made
219 : * available to the NaCl module.
220 : *
221 : * In order to address this issue, we (1) make descriptors that can
222 : * transfer other descriptors non-transferable, and (2) we use locking
223 : * so that only one thread can be performing (the low level bits of)
224 : * imc_msgsend at a time. The non-transferability of such descriptors
225 : * addresses the multi-module scenario, as opposed to the
226 : * multi-threaded scenario, where a sender race cause receiver
227 : * confusion.
228 : */
229 : static ssize_t NaClDescImcDescLowLevelSendMsg(
230 : struct NaClDesc *vself,
231 : struct NaClMessageHeader const *dgram,
232 4 : int flags) {
233 : struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *)
234 4 : vself);
235 : int result;
236 :
237 4 : NaClXMutexLock(&self->sendmsg_mu);
238 4 : result = NaClSendDatagram(self->base.h, dgram, flags);
239 4 : NaClXMutexUnlock(&self->sendmsg_mu);
240 :
241 4 : if (-1 == result) {
242 : #if NACL_WINDOWS
243 0 : return -NaClXlateSystemError(GetLastError());
244 : #elif NACL_LINUX || NACL_OSX
245 : return -NaClXlateErrno(errno);
246 : #else
247 : # error "Unknown target platform: cannot translate error code(s) from SendMsg"
248 : #endif
249 : }
250 4 : return result;
251 4 : }
252 :
253 :
254 : /*
255 : * NaClDescXferableDataDescLowLevelSendMsg implements imc_sendmsg For
256 : * data-only descriptors. We assume that whatever protocol exists at
257 : * the NaClSendDatagram level is still not thread safe, but that the
258 : * lack of thread safety will not have a significant impact on
259 : * security. This is still somewhat brittle: the low-level Windows
260 : * IMC code can be convinced to do the wrong thing still via sender
261 : * races, but the receiver, because it expects zero handles, will have
262 : * called ReceiveDatagram in such a way that any "received handles"
263 : * are closed. This implies that arbitrary Windows handles can be
264 : * made to be closed, including those not accessible by NaCl modules,
265 : * but fortunately this should only result in a denial of service as
266 : * the error caused by the use of an invalid handle is detected by the
267 : * service runtime and cause an abort.
268 : *
269 : * Note that it is still an application error to send or receive data
270 : * with a transferable data-only descriptor from two threads (or two
271 : * modules) simultaneously.
272 : */
273 : static ssize_t
274 : NaClDescXferableDataDescLowLevelSendMsg(struct NaClDesc *vself,
275 : struct NaClMessageHeader const *dgram,
276 0 : int flags) {
277 : struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *)
278 0 : vself);
279 : int result;
280 :
281 0 : if (0 != dgram->handle_count) {
282 : /*
283 : * A transferable descriptor cannot be used to transfer other
284 : * descriptors.
285 : */
286 : NaClLog(2,
287 : ("NaClDescXferableDataDescLowLevelSendMsg: tranferable and"
288 0 : " non-zero handle_count\n"));
289 0 : return -NACL_ABI_EINVAL;
290 : }
291 :
292 0 : result = NaClSendDatagram(self->base.h, dgram, flags);
293 :
294 0 : if (-1 == result) {
295 : #if NACL_WINDOWS
296 0 : return -NaClXlateSystemError(GetLastError());
297 : #elif NACL_LINUX || NACL_OSX
298 : return -NaClXlateErrno(errno);
299 : #else
300 : # error "Unknown target platform: cannot translate error code(s) from SendMsg"
301 : #endif
302 : }
303 0 : return result;
304 0 : }
305 :
306 :
307 : /*
308 : * See discussion at NaClDescImcDescLowLevelSendMsg for details. An
309 : * imc_recvmsg race is not substantively different from an imc_sendmsg
310 : * race.
311 : */
312 : static ssize_t NaClDescImcDescLowLevelRecvMsg(struct NaClDesc *vself,
313 : struct NaClMessageHeader *dgram,
314 4 : int flags) {
315 : struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *)
316 4 : vself);
317 : int result;
318 :
319 4 : NaClLog(4, "Entered NaClDescImcDescLowLevelRecvMsg, h=%d\n", self->base.h);
320 4 : NaClXMutexLock(&self->recvmsg_mu);
321 4 : result = NaClReceiveDatagram(self->base.h, dgram, flags);
322 4 : NaClXMutexUnlock(&self->recvmsg_mu);
323 :
324 4 : if (-1 == result) {
325 : #if NACL_WINDOWS
326 2 : return -NaClXlateSystemError(GetLastError());
327 : #elif NACL_LINUX || NACL_OSX
328 : return -errno;
329 : #else
330 : # error "Unknown target platform: cannot translate error code(s) from RecvMsg"
331 : #endif
332 : }
333 4 : return result;
334 4 : }
335 :
336 :
337 : /*
338 : * See discussion at NaClDescXferableDataDescLowLevelSendMsg for details. An
339 : * imc_recvmsg race is not substantively different from an imc_sendmsg
340 : * race.
341 : */
342 : static ssize_t NaClDescXferableDataDescLowLevelRecvMsg(
343 : struct NaClDesc *vself,
344 : struct NaClMessageHeader *dgram,
345 0 : int flags) {
346 : struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *)
347 0 : vself);
348 : int result;
349 :
350 : NaClLog(4, "Entered NaClDescXferableDataDescLowLevelRecvMsg, h = %d\n",
351 0 : self->base.h);
352 0 : if (0 != dgram->handle_count) {
353 : /*
354 : * A transferable descriptor is data-only, and it is an error to
355 : * try to receive any I/O descriptors with it.
356 : */
357 : NaClLog(2,
358 : "NaClDescXferableDataDescLowLevelRecvMsg:"
359 0 : " tranferable and non-zero handle_count\n");
360 0 : return -NACL_ABI_EINVAL;
361 : }
362 :
363 0 : result = NaClReceiveDatagram(self->base.h, dgram, flags);
364 :
365 0 : if (-1 == result) {
366 : #if NACL_WINDOWS
367 0 : return -NaClXlateSystemError(GetLastError());
368 : #elif NACL_LINUX || NACL_OSX
369 : return -errno;
370 : #else
371 : # error "Unknown target platform: cannot translate error code(s) from RecvMsg"
372 : #endif
373 : }
374 0 : return result;
375 0 : }
376 :
377 :
378 : static struct NaClDescVtbl const kNaClDescImcConnectedDescVtbl = {
379 : {
380 : NaClDescImcConnectedDescDtor,
381 : },
382 : NaClDescMapNotImplemented,
383 : NACL_DESC_UNMAP_NOT_IMPLEMENTED
384 : NaClDescReadNotImplemented,
385 : NaClDescWriteNotImplemented,
386 : NaClDescSeekNotImplemented,
387 : NaClDescPReadNotImplemented,
388 : NaClDescPWriteNotImplemented,
389 : NaClDescFstatNotImplemented,
390 : NaClDescGetdentsNotImplemented,
391 : NaClDescExternalizeSizeNotImplemented,
392 : NaClDescExternalizeNotImplemented,
393 : NaClDescLockNotImplemented,
394 : NaClDescTryLockNotImplemented,
395 : NaClDescUnlockNotImplemented,
396 : NaClDescWaitNotImplemented,
397 : NaClDescTimedWaitAbsNotImplemented,
398 : NaClDescSignalNotImplemented,
399 : NaClDescBroadcastNotImplemented,
400 : NaClDescSendMsgNotImplemented,
401 : NaClDescRecvMsgNotImplemented,
402 : NaClDescLowLevelSendMsgNotImplemented,
403 : NaClDescLowLevelRecvMsgNotImplemented,
404 : NaClDescConnectAddrNotImplemented,
405 : NaClDescAcceptConnNotImplemented,
406 : NaClDescPostNotImplemented,
407 : NaClDescSemWaitNotImplemented,
408 : NaClDescGetValueNotImplemented,
409 : NaClDescSetMetadata,
410 : NaClDescGetMetadata,
411 : NaClDescSetFlags,
412 : NaClDescGetFlags,
413 : NaClDescIsattyNotImplemented,
414 : NACL_DESC_CONNECTED_SOCKET,
415 : };
416 :
417 :
418 : static struct NaClDescVtbl const kNaClDescImcDescVtbl = {
419 : {
420 : NaClDescImcDescDtor, /* diff */
421 : },
422 : NaClDescMapNotImplemented,
423 : NACL_DESC_UNMAP_NOT_IMPLEMENTED
424 : NaClDescReadNotImplemented,
425 : NaClDescWriteNotImplemented,
426 : NaClDescSeekNotImplemented,
427 : NaClDescPReadNotImplemented,
428 : NaClDescPWriteNotImplemented,
429 : NaClDescImcDescFstat, /* diff */
430 : NaClDescGetdentsNotImplemented,
431 : NaClDescExternalizeSizeNotImplemented,
432 : NaClDescExternalizeNotImplemented,
433 : NaClDescLockNotImplemented,
434 : NaClDescTryLockNotImplemented,
435 : NaClDescUnlockNotImplemented,
436 : NaClDescWaitNotImplemented,
437 : NaClDescTimedWaitAbsNotImplemented,
438 : NaClDescSignalNotImplemented,
439 : NaClDescBroadcastNotImplemented,
440 : NaClImcSendTypedMessage, /* diff */
441 : NaClImcRecvTypedMessage, /* diff */
442 : NaClDescImcDescLowLevelSendMsg, /* diff */
443 : NaClDescImcDescLowLevelRecvMsg, /* diff */
444 : NaClDescConnectAddrNotImplemented,
445 : NaClDescAcceptConnNotImplemented,
446 : NaClDescPostNotImplemented,
447 : NaClDescSemWaitNotImplemented,
448 : NaClDescGetValueNotImplemented,
449 : NaClDescSetMetadata,
450 : NaClDescGetMetadata,
451 : NaClDescSetFlags,
452 : NaClDescGetFlags,
453 : NaClDescIsattyNotImplemented,
454 : NACL_DESC_IMC_SOCKET, /* diff */
455 : };
456 :
457 :
458 : static struct NaClDescVtbl const kNaClDescXferableDataDescVtbl = {
459 : {
460 : NaClDescXferableDataDescDtor, /* diff */
461 : },
462 : NaClDescMapNotImplemented,
463 : NACL_DESC_UNMAP_NOT_IMPLEMENTED
464 : NaClDescReadNotImplemented,
465 : NaClDescWriteNotImplemented,
466 : NaClDescSeekNotImplemented,
467 : NaClDescPReadNotImplemented,
468 : NaClDescPWriteNotImplemented,
469 : NaClDescXferableDataDescFstat, /* diff */
470 : NaClDescGetdentsNotImplemented,
471 : NaClDescXferableDataDescExternalizeSize, /* diff */
472 : NaClDescXferableDataDescExternalize, /* diff */
473 : NaClDescLockNotImplemented,
474 : NaClDescTryLockNotImplemented,
475 : NaClDescUnlockNotImplemented,
476 : NaClDescWaitNotImplemented,
477 : NaClDescTimedWaitAbsNotImplemented,
478 : NaClDescSignalNotImplemented,
479 : NaClDescBroadcastNotImplemented,
480 : NaClImcSendTypedMessage, /* diff */
481 : NaClImcRecvTypedMessage, /* diff */
482 : NaClDescXferableDataDescLowLevelSendMsg, /* diff */
483 : NaClDescXferableDataDescLowLevelRecvMsg, /* diff */
484 : NaClDescConnectAddrNotImplemented,
485 : NaClDescAcceptConnNotImplemented,
486 : NaClDescPostNotImplemented,
487 : NaClDescSemWaitNotImplemented,
488 : NaClDescGetValueNotImplemented,
489 : NaClDescSetMetadata,
490 : NaClDescGetMetadata,
491 : NaClDescSetFlags,
492 : NaClDescGetFlags,
493 : NaClDescIsattyNotImplemented,
494 : NACL_DESC_TRANSFERABLE_DATA_SOCKET, /* diff */
495 : };
496 :
497 :
498 : int NaClDescXferableDataDescInternalize(
499 : struct NaClDesc **baseptr,
500 : struct NaClDescXferState *xfer,
501 0 : struct NaClDescQuotaInterface *quota_interface) {
502 : int rv;
503 : struct NaClDescXferableDataDesc *ndxdp;
504 :
505 : UNREFERENCED_PARAMETER(quota_interface);
506 0 : NaClLog(4, "Entered NaClDescXferableDataDescInternalize\n");
507 :
508 0 : ndxdp = malloc(sizeof *ndxdp);
509 0 : if (NULL == ndxdp) {
510 : NaClLog(LOG_ERROR,
511 0 : "NaClXferableDataDescInternalize: no memory\n");
512 0 : rv = -NACL_ABI_ENOMEM;
513 0 : goto cleanup;
514 : }
515 0 : rv = NaClDescInternalizeCtor((struct NaClDesc *) ndxdp, xfer);
516 0 : if (!rv) {
517 0 : free(ndxdp);
518 0 : ndxdp = NULL;
519 0 : goto cleanup;
520 : }
521 :
522 0 : if (xfer->next_handle == xfer->handle_buffer_end) {
523 : NaClLog(LOG_ERROR,
524 : ("NaClXferableDataDescInternalize: no descriptor"
525 0 : " left in xfer state\n"));
526 0 : rv = -NACL_ABI_EIO;
527 0 : goto cleanup;
528 : }
529 : if (!NaClDescXferableDataDescSubclassesCtor(ndxdp,
530 0 : *xfer->next_handle)) {
531 : NaClLog(LOG_ERROR,
532 0 : "NaClXferableDataDescInternalize: descriptor ctor error\n");
533 0 : rv = -NACL_ABI_EIO;
534 0 : goto cleanup;
535 : }
536 0 : *xfer->next_handle++ = NACL_INVALID_HANDLE;
537 0 : *baseptr = (struct NaClDesc *) ndxdp;
538 0 : rv = 0;
539 :
540 : cleanup:
541 0 : if (rv < 0) {
542 0 : NaClDescSafeUnref((struct NaClDesc *) ndxdp);
543 : }
544 0 : return rv;
545 0 : }
|