/* * NaClDescXferableDataDescLowLevelSendMsg implements imc_sendmsg For * data-only descriptors. We assume that whatever protocol exists at * the NaClSendDatagram level is still not thread safe, but that the * lack of thread safety will not have a significant impact on * security. This is still somewhat brittle: the low-level Windows * IMC code can be convinced to do the wrong thing still via sender * races, but the receiver, because it expects zero handles, will have * called ReceiveDatagram in such a way that any "received handles" * are closed. This implies that arbitrary Windows handles can be * made to be closed, including those not accessible by NaCl modules, * but fortunately this should only result in a denial of service as * the error caused by the use of an invalid handle is detected by the * service runtime and cause an abort. * * Note that it is still an application error to send or receive data * with a transferable data-only descriptor from two threads (or two * modules) simultaneously. */ static ssize_t NaClDescXferableDataDescLowLevelSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *) vself); int result; if (0 != dgram->handle_count) { /* * A transferable descriptor cannot be used to transfer other * descriptors. */ NaClLog(2, ("NaClDescXferableDataDescLowLevelSendMsg: tranferable and" " non-zero handle_count\n")); return -NACL_ABI_EINVAL; } result = NaClSendDatagram(self->base.h, dgram, flags); if (-1 == result) { #if NACL_WINDOWS return -NaClXlateSystemError(GetLastError()); #elif NACL_LINUX || NACL_OSX return -NaClXlateErrno(errno); #else # error "Unknown target platform: cannot translate error code(s) from SendMsg" #endif } return result; }
/* * NaClDescXferableDataDescSendMsg implements imc_sendmsg For * data-only descriptors. We assume that whatever protocol exists at * the NaClSendDatagram level is still not thread safe, but that the * lack of thread safety will not have a significant impact on * security. This is still somewhat brittle: the low-level Windows * IMC code can be convinced to do the wrong thing still via sender * races, but the receiver, because it expects zero handles, will have * called ReceiveDatagram in such a way that any "received handles" * are closed. This implies that arbitrary Windows handles can be * made to be closed, including those not accessible by NaCl modules, * but fortunately this should only result in a denial of service as * the error caused by the use of an invalid handle is detected by the * service runtime and cause an abort. * * Note that it is still an application error to send or receive data * with a transferable data-only descriptor from two threads (or two * modules) simultaneously. */ static ssize_t NaClDescXferableDataDescSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescXferableDataDesc *self = ((struct NaClDescXferableDataDesc *) vself); int result; if (0 != dgram->handle_count) { /* * A transferable descriptor cannot be used to transfer other * descriptors. */ NaClLog(2, ("NaClDescXferableDataDescSendMsg: tranferable and" " non-zero handle_count\n")); return -NACL_ABI_EINVAL; } result = NaClSendDatagram(self->base.h, dgram, flags); if (-1 == result) { return -NaClXlateErrno(errno); } return result; }
/* * In the level of NaClDescImcDescSendMsg, we do not know what * protocol is implemented by NaClSendDatagram (and indeed, in the * Windows implementation, the access rights transfer involves a more * complex protocol to get the peer process id). Because the * underlying low-level IMC implementation does not provide thread * safety, this is the source of race conditions: two simultaneous * imc_sendmsg syscalls to the same descriptor could get their various * protcol bits interleaved, so the receiver will get garbled data * that cause the wrong underlying host OS descriptor to be made * available to the NaCl module. * * In order to address this issue, we (1) make descriptors that can * transfer other descriptors non-transferable, and (2) we use locking * so that only one thread can be performing (the low level bits of) * imc_msgsend at a time. The non-transferability of such descriptors * addresses the multi-module scenario, as opposed to the * multi-threaded scenario, where a sender race cause receiver * confusion. */ static ssize_t NaClDescImcDescSendMsg(struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *) vself); int result; NaClXMutexLock(&self->sendmsg_mu); result = NaClSendDatagram(self->base.h, dgram, flags); NaClXMutexUnlock(&self->sendmsg_mu); if (-1 == result) { return -NaClXlateErrno(errno); } return result; }
int main(int argc, char* argv[]) { int result; NaClHandle pair[2]; NaClMessageHeader header; NaClIOVec vec; char buffer[] = "Hello!"; g_front = NaClBoundSocket(&test_address); if (g_front == NACL_INVALID_HANDLE) { PrintError("BoundSocket"); exit(EXIT_FAILURE); } atexit(CleanUp); if (NaClSocketPair(pair) != 0) { PrintError("SocketPair"); exit(EXIT_FAILURE); } vec.base = buffer; vec.length = sizeof buffer; /* Test SendDatagram */ header.iov = &vec; header.iov_length = 1; header.handles = NULL; header.handle_count = 0; result = NaClSendDatagram(pair[0], &header, 0); assert(result == sizeof buffer); /* Test ReceiveDatagram */ memset(buffer, 0, sizeof buffer); header.iov = &vec; header.iov_length = 1; header.handles = NULL; header.handle_count = 0; result = NaClReceiveDatagram(pair[1], &header, 0); assert(result == sizeof buffer); assert(strcmp(buffer, "Hello!") == 0); printf("%s\n", buffer); (void) NaClClose(pair[0]); (void) NaClClose(pair[1]); return 0; }
/* * In the level of NaClDescImcDescLowLevelSendMsg, we do not know what * protocol is implemented by NaClSendDatagram (and indeed, in the * Windows implementation, the access rights transfer involves a more * complex protocol to get the peer process id). Because the * underlying low-level IMC implementation does not provide thread * safety, this is the source of race conditions: two simultaneous * imc_sendmsg syscalls to the same descriptor could get their various * protcol bits interleaved, so the receiver will get garbled data * that cause the wrong underlying host OS descriptor to be made * available to the NaCl module. * * In order to address this issue, we (1) make descriptors that can * transfer other descriptors non-transferable, and (2) we use locking * so that only one thread can be performing (the low level bits of) * imc_msgsend at a time. The non-transferability of such descriptors * addresses the multi-module scenario, as opposed to the * multi-threaded scenario, where a sender race cause receiver * confusion. */ static ssize_t NaClDescImcDescLowLevelSendMsg( struct NaClDesc *vself, struct NaClMessageHeader const *dgram, int flags) { struct NaClDescImcDesc *self = ((struct NaClDescImcDesc *) vself); int result; NaClXMutexLock(&self->sendmsg_mu); result = NaClSendDatagram(self->base.h, dgram, flags); NaClXMutexUnlock(&self->sendmsg_mu); if (-1 == result) { #if NACL_WINDOWS return -NaClXlateSystemError(GetLastError()); #elif NACL_LINUX || NACL_OSX return -NaClXlateErrno(errno); #else # error "Unknown target platform: cannot translate error code(s) from SendMsg" #endif } return result; }