Last active
December 19, 2015 01:09
-
-
Save Amanieu/5873672 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef _WIN32 | |
#include <windows.h> | |
#else | |
#include <unistd.h> | |
#include <sys/mman.h> | |
#include <sys/wait.h> | |
#include <signal.h> | |
#endif | |
#include <stdio.h> | |
#include <string.h> | |
#include "native_client/src/shared/imc/nacl_imc_c.h" | |
#include "native_client/src/public/imc_types.h" | |
#include "wrapper.h" | |
// File handle for the root socket | |
#define ROOT_SOCKET_FD 100 | |
// sel_ldr and irt paths | |
#define SEL_LDR "tools/sel_ldr_x86_64" | |
#define IRT "tools/irt_core_x86_64.nexe" | |
// Definitions taken from nacl_desc_base.h | |
enum NaClDescTypeTag { | |
NACL_DESC_INVALID, | |
NACL_DESC_DIR, | |
NACL_DESC_HOST_IO, | |
NACL_DESC_CONN_CAP, | |
NACL_DESC_CONN_CAP_FD, | |
NACL_DESC_BOUND_SOCKET, | |
NACL_DESC_CONNECTED_SOCKET, | |
NACL_DESC_SHM, | |
NACL_DESC_SYSV_SHM, | |
NACL_DESC_MUTEX, | |
NACL_DESC_CONDVAR, | |
NACL_DESC_SEMAPHORE, | |
NACL_DESC_SYNC_SOCKET, | |
NACL_DESC_TRANSFERABLE_DATA_SOCKET, | |
NACL_DESC_IMC_SOCKET, | |
NACL_DESC_QUOTA, | |
NACL_DESC_DEVICE_RNG, | |
NACL_DESC_DEVICE_POSTMESSAGE, | |
NACL_DESC_CUSTOM, | |
NACL_DESC_NULL | |
}; | |
#define NACL_DESC_TYPE_MAX (NACL_DESC_NULL + 1) | |
#define NACL_DESC_TYPE_END_TAG (0xff) | |
struct NaClInternalRealHeader { | |
uint32_t xfer_protocol_version; | |
uint32_t descriptor_data_bytes; | |
}; | |
struct NaClInternalHeader { | |
struct NaClInternalRealHeader h; | |
char pad[((sizeof(struct NaClInternalRealHeader) + 0x10) & ~0xf) | |
- sizeof(struct NaClInternalRealHeader)]; | |
}; | |
#define NACL_HANDLE_TRANSFER_PROTOCOL 0xd3c0de01 | |
namespace NaClWrapper { | |
int InternalSendMsg(OSHandleType handle, const void* data, size_t len, IPCHandle* const* handles, size_t num_handles) | |
{ | |
NaClMessageHeader hdr; | |
NaClIOVec iov[3]; | |
NaClHandle h[MSG_MAX_HANDLES]; | |
if (num_handles > MSG_MAX_HANDLES) | |
return -1; | |
if (len > MSG_MAX_BYTES) | |
return -1; | |
for (int i = 0; i < num_handles; i++) | |
h[i] = handles[i]->handle; | |
hdr.iov = iov; | |
hdr.handles = h; | |
hdr.handle_count = num_handles; | |
hdr.flags = 0; | |
#ifdef __native_client__ | |
hdr.iov_length = 1; | |
iov[0].base = const_cast<void*>(data); | |
iov[0].length = len; | |
return NaClSendDatagram(handle, &hdr, 0); | |
#else | |
size_t desc_bytes = 0; | |
char* desc_buffer = NULL; | |
if (num_handles != 0) { | |
for (int i = 0; i < num_handles; i++) { | |
// tag: 1 byte | |
// flags: 4 bytes | |
// size: 8 bytes (only for SHM) | |
desc_bytes++; | |
desc_bytes += sizeof(uint32_t); | |
if (handles[i]->type == IPCHandle::TYPE_SHM) | |
desc_bytes += sizeof(uint64_t); | |
} | |
// Add 1 byte end tag and round to 16 bytes | |
desc_bytes = (desc_bytes + 1 + 0xf) & ~0xf; | |
desc_buffer = new char[desc_bytes]; | |
char* desc_buffer_ptr = desc_buffer; | |
for (int i = 0; i < num_handles; i++) { | |
if (handles[i]->type == IPCHandle::TYPE_SOCKET) { | |
*desc_buffer_ptr++ = NACL_DESC_TRANSFERABLE_DATA_SOCKET; | |
memset(desc_buffer_ptr, 0, sizeof(uint32_t)); | |
desc_buffer_ptr += sizeof(uint32_t); | |
} else { | |
*desc_buffer_ptr++ = NACL_DESC_SHM; | |
memset(desc_buffer_ptr, 0, sizeof(uint32_t)); | |
desc_buffer_ptr += sizeof(uint32_t); | |
memcpy(desc_buffer_ptr, &handles[i]->size, sizeof(uint64_t)); | |
desc_buffer_ptr += sizeof(uint64_t); | |
} | |
} | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
*desc_buffer_ptr++ = NACL_DESC_TYPE_END_TAG; | |
// Clear any padding bytes to avoid information leak | |
memset(desc_buffer_ptr, desc_buffer + desc_bytes - desc_buffer_ptr, 0); | |
} | |
NaClInternalHeader internal_hdr = {{NACL_HANDLE_TRANSFER_PROTOCOL, desc_bytes}}; | |
hdr.iov_length = 3; | |
iov[0].base = &internal_hdr; | |
iov[0].length = sizeof(NaClInternalHeader); | |
iov[1].base = desc_buffer; | |
iov[1].length = desc_bytes; | |
iov[2].base = const_cast<void*>(data); | |
iov[2].length = len; | |
int result = NaClSendDatagram(handle, &hdr, 0); | |
delete[] desc_buffer; | |
// Return -1 if the headers didn't transmit completely | |
if (result - desc_bytes - sizeof(NaClInternalHeader) < 0) | |
return -1; | |
else | |
return result - desc_bytes - sizeof(NaClInternalHeader); | |
#endif | |
} | |
int InternalRecvMsg(OSHandleType handle, void* buffer, size_t len, IPCHandle** handles, size_t num_handles) | |
{ | |
NaClMessageHeader hdr; | |
NaClIOVec iov[2]; | |
NaClHandle h[NACL_ABI_IMC_DESC_MAX]; | |
if (num_handles > MSG_MAX_HANDLES) | |
return -1; | |
if (len > MSG_MAX_BYTES) | |
len = MSG_MAX_BYTES; | |
for (int i = 0; i < num_handles; i++) { | |
h[i] = NACL_INVALID_HANDLE; | |
handles[i] = NULL; | |
} | |
hdr.iov = iov; | |
hdr.handles = h; | |
hdr.flags = 0; | |
#ifdef __native_client__ | |
hdr.handle_count = num_handles; | |
hdr.iov_length = 1; | |
iov[0].base = buffer; | |
iov[0].length = len; | |
int result = NaClReceiveDatagram(handle, &hdr, 0); | |
if (result < 0) | |
return result; | |
for (int i = 0; i < num_handles; i++) { | |
if (h[i] != NACL_INVALID_HANDLE) { | |
handles[i] = new IPCHandle; | |
handles[i]->handle = h[i]; | |
} | |
} | |
return result; | |
#else | |
NaClInternalHeader internal_hdr; | |
char* recv_buffer = new char[NACL_ABI_IMC_BYTES_MAX]; | |
hdr.handle_count = NACL_ABI_IMC_DESC_MAX; | |
hdr.iov_length = 2; | |
iov[0].base = &internal_hdr; | |
iov[0].length = sizeof(NaClInternalHeader); | |
iov[1].base = recv_buffer; | |
iov[1].length = NACL_ABI_IMC_BYTES_MAX; | |
int result = NaClReceiveDatagram(handle, &hdr, 0); | |
char* desc_ptr = recv_buffer; | |
int handle_index = 0; | |
if (result < sizeof(NaClInternalHeader)) | |
goto fail; | |
result -= sizeof(NaClInternalHeader); | |
if (internal_hdr.h.xfer_protocol_version != NACL_HANDLE_TRANSFER_PROTOCOL || result < internal_hdr.h.descriptor_data_bytes) | |
goto fail; | |
result -= internal_hdr.h.descriptor_data_bytes; | |
while (desc_ptr < recv_buffer + internal_hdr.h.descriptor_data_bytes) { | |
char tag = *desc_ptr++; | |
if (tag == NACL_DESC_TYPE_END_TAG) | |
break; | |
if (tag != NACL_DESC_SHM && tag != NACL_DESC_TRANSFERABLE_DATA_SOCKET) | |
goto fail; | |
// Ignore flags | |
if (recv_buffer + internal_hdr.h.descriptor_data_bytes - desc_ptr < sizeof(uint32_t)) | |
goto fail; | |
desc_ptr += sizeof(uint32_t); | |
uint64_t size; | |
if (tag == NACL_DESC_SHM) { | |
if (recv_buffer + internal_hdr.h.descriptor_data_bytes - desc_ptr < sizeof(uint64_t)) | |
goto fail; | |
memcpy(&size, desc_ptr, sizeof(uint64_t)); | |
desc_ptr += sizeof(uint64_t); | |
} | |
int i = handle_index++; | |
if (i >= num_handles) { | |
// Make sure we don't leak the handle if more handles are sent than the user requested | |
// We will leak if more handles are sent than the ABI allows, but that should never happen | |
NaClClose(h[i]); | |
h[i] = NACL_INVALID_HANDLE; | |
} else { | |
handles[i] = new IPCHandle; | |
handles[i]->handle = h[i]; | |
h[i] = NACL_INVALID_HANDLE; | |
if (tag == NACL_DESC_TRANSFERABLE_DATA_SOCKET) | |
handles[i]->type = IPCHandle::TYPE_SOCKET; | |
else { | |
handles[i]->type = IPCHandle::TYPE_SHM; | |
handles[i]->size = size; | |
} | |
} | |
} | |
if (len > result) | |
len = result; | |
memcpy(buffer, recv_buffer + internal_hdr.h.descriptor_data_bytes, len); | |
delete[] recv_buffer; | |
return result; | |
// If an error occurred, make sure all resources are freed | |
fail: | |
delete[] recv_buffer; | |
for (int i = 0; i < NACL_ABI_IMC_DESC_MAX; i++) { | |
if (h[i] != NACL_INVALID_HANDLE) | |
NaClClose(h[i]); | |
} | |
for (int i = 0; i < num_handles; i++) { | |
if (handles[i]) | |
handles[i]->Close(); | |
handles[i] = NULL; | |
} | |
return -1; | |
#endif | |
} | |
IPCHandle::~IPCHandle() | |
{ | |
NaClClose(handle); | |
} | |
int IPCHandle::SendMsg(const void* data, size_t len) | |
{ | |
return InternalSendMsg(handle, data, len, NULL, 0); | |
} | |
int IPCHandle::RecvMsg(void* buffer, size_t len) | |
{ | |
return InternalRecvMsg(handle, buffer, len, NULL, 0); | |
} | |
void* IPCHandle::Map(uint64_t offset, size_t size) | |
{ | |
// We don't use NaClMap here because it only supports MAP_FIXED | |
#ifdef _WIN32 | |
return MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, offset >> 32, offset & 0xFFFFFFF, size); | |
#else | |
void *result = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle, offset); | |
if (result == MAP_FAILED) | |
return NULL; | |
else | |
return result; | |
#endif | |
} | |
void Unmap(void* addr, size_t size) | |
{ | |
#ifdef _WIN32 | |
UnmapViewOfFile(addr); | |
#else | |
munmap(addr, size); | |
#endif | |
} | |
bool SocketPair(IPCHandle*& first, IPCHandle*& second) | |
{ | |
NaClHandle handles[2]; | |
if (NaClSocketPair(handles) != 0) | |
return false; | |
first = new IPCHandle; | |
second = new IPCHandle; | |
first->handle = handles[0]; | |
second->handle = handles[1]; | |
#ifndef __native_client__ | |
first->type = second->type = IPCHandle::TYPE_SOCKET; | |
#endif | |
return true; | |
} | |
IPCHandle* CreateSharedMemory(size_t size) | |
{ | |
OSHandleType h = NaClCreateMemoryObject(size, 0); | |
if (h == NACL_INVALID_HANDLE) | |
return NULL; | |
IPCHandle* out = new IPCHandle; | |
out->handle = h; | |
#ifndef __native_client__ | |
out->type = IPCHandle::TYPE_SHM; | |
out->size = size; | |
#endif | |
return out; | |
} | |
#ifndef __native_client__ | |
Module::~Module() | |
{ | |
NaClClose(root_socket); | |
#ifdef _WIN32 | |
TerminateProcess(process_handle, 0); | |
CloseHandle(process_handle); | |
#else | |
kill(process_handle, 9); | |
int status; | |
waitpid(process_handle, &status, 0); | |
#endif | |
} | |
int Module::SendMsg(const void* data, size_t len, IPCHandle* const* handles, size_t num_handles) | |
{ | |
return InternalSendMsg(root_socket, data, len, handles, num_handles); | |
} | |
int Module::RecvMsg(void* buffer, size_t len, IPCHandle** handles, size_t num_handles) | |
{ | |
return InternalRecvMsg(root_socket, buffer, len, handles, num_handles); | |
} | |
Module* LoadModule(const char* path) | |
{ | |
#ifdef _WIN32 | |
NaClHandle pair[2]; | |
if (NaClSocketPair(pair)) | |
return NULL; | |
if (!SetHandleInformation(pair[1], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { | |
NaClClose(pair[0]); | |
NaClClose(pair[1]); | |
return NULL; | |
} | |
char cmdline[1024]; | |
int r = snprintf(cmdline, sizeof(cmdline), "\"%s\" -B \"%s\" -i %d:%ld -- \"%s\"", SEL_LDR, IRT, ROOT_SOCKET_FD, reinterpret_cast<long>(pair[1]), path); | |
if (r >= sizeof(cmdline)) { | |
NaClClose(pair[0]); | |
NaClClose(pair[1]); | |
return NULL; | |
} | |
STARTUPINFOA startup_info; | |
PROCESS_INFORMATION process_information; | |
memset(&startup_info, 0, sizeof(startup_info)); | |
startup_info.cb = sizeof(startup_info); | |
if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_information)) { | |
NaClClose(pair[0]); | |
NaClClose(pair[1]); | |
return NULL; | |
} | |
NaClClose(pair[1]); | |
CloseHandle(process_information.hThread); | |
Module* out = new Module; | |
out->process_handle = process_information.hProcess; | |
out->root_socket = pair[0]; | |
return out; | |
#else | |
NaClHandle pair[2]; | |
if (NaClSocketPair(pair)) | |
return NULL; | |
char arg[32]; | |
snprintf(arg, sizeof(arg), "%d:%d", ROOT_SOCKET_FD, pair[1]); | |
int pid = fork(); | |
if (pid == 0) { | |
NaClClose(pair[0]); | |
execl(SEL_LDR, SEL_LDR, "-B", IRT, "-i", arg, "--", path, NULL); | |
} | |
NaClClose(pair[1]); | |
Module* out = new Module; | |
out->process_handle = pid; | |
out->root_socket = pair[0]; | |
return out; | |
#endif | |
} | |
#endif | |
#ifdef __native_client__ | |
namespace RootSocket { | |
int SendMsg(const void* data, size_t len, IPCHandle* const* handles, size_t num_handles) | |
{ | |
return InternalSendMsg(ROOT_SOCKET_FD, data, len, handles, num_handles); | |
} | |
int RecvMsg(void* buffer, size_t len, IPCHandle** handles, size_t num_handles) | |
{ | |
return InternalRecvMsg(ROOT_SOCKET_FD, buffer, len, handles, num_handles); | |
} | |
} | |
#endif | |
} // namespace NaClWrapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stddef.h> | |
#include <stdint.h> | |
namespace NaClWrapper { | |
// Operating system handle type | |
#ifdef _WIN32 | |
// HANDLE is defined as void* in windows.h | |
typedef void* OSHandleType; | |
#else | |
typedef int OSHandleType; | |
#endif | |
// Maximum number of bytes that can be sent in a message | |
static const size_t MSG_MAX_BYTES = 128 << 10; | |
// Maximum number of handles that can be sent in a message | |
static const size_t MSG_MAX_HANDLES = 8; | |
// IPC object base class, can represent either a socket or a shared memory area | |
class IPCHandle { | |
public: | |
// Close the handle | |
~IPCHandle(); | |
void Close() | |
{ | |
delete this; | |
} | |
// Send a message through the socket | |
// Returns the number of bytes sent or -1 on error | |
int SendMsg(const void* data, size_t len); | |
// Recieve a message from the socket, will block until a message arrives. | |
// Returns the number of bytes sent or -1 on error | |
int RecvMsg(void* buffer, size_t len); | |
// Map a portion of the shared memory region | |
// Returns a pointer to the memory mapping or NULL on error | |
// The mapping remains valid even after the handle is closed, call UnMap to release it. | |
void* Map(uint64_t offset, size_t size); | |
private: | |
OSHandleType handle; | |
// Handles are non-copyable | |
IPCHandle() {} | |
IPCHandle(const IPCHandle&); | |
IPCHandle& operator=(const IPCHandle&); | |
friend int InternalSendMsg(OSHandleType handle, const void* data, size_t len, IPCHandle* const* handles, size_t num_handles); | |
friend int InternalRecvMsg(OSHandleType handle, void* buffer, size_t len, IPCHandle** handles, size_t num_handles); | |
friend bool SocketPair(IPCHandle*& first, IPCHandle*& second); | |
friend IPCHandle* CreateSharedMemory(size_t size); | |
// Information only required on the host side for handle transfer protocol | |
#ifndef __native_client__ | |
enum HandleType { | |
TYPE_SOCKET, | |
TYPE_SHM | |
}; | |
HandleType type; | |
uint64_t size; | |
#endif | |
}; | |
// Unmap a shared memory region. addr must be a pointer returned by IPCHandle::Map. | |
void Unmap(void* addr, size_t size); | |
// Create a pair of sockets which can IPCHandle with each other. | |
// Returns false on error | |
bool SocketPair(IPCHandle*& first, IPCHandle*& second); | |
// Allocate a shared memory region of the specified size. | |
IPCHandle* CreateSharedMemory(size_t size); | |
// Host-only definitions | |
#ifndef __native_client__ | |
class Module { | |
public: | |
// Close the module and kill the NaCl process | |
~Module(); | |
void Close() | |
{ | |
delete this; | |
} | |
// Send a message through the root socket, optionally with a some IPC handles. | |
// Returns the number of bytes sent or -1 on error | |
int SendMsg(const void* data, size_t len, IPCHandle* const* handles, size_t num_handles); | |
// Recieve a message from the root socket, will block until a message arrives. | |
// Up to num_handles handles are retrieved from the message, any further handles are discarded. | |
// If there are less than num_handles handles, the remaining entries are filled with NULL pointers. | |
// Returns the number of bytes recieved or -1 on error | |
int RecvMsg(void* buffer, size_t len, IPCHandle** handles, size_t num_handles); | |
// Overloads for sending data without any handles | |
int SendMsg(const void* data, size_t len) | |
{ | |
return SendMsg(data, len, NULL, 0); | |
} | |
int RecvMsg(void* buffer, size_t len) | |
{ | |
return RecvMsg(buffer, len, NULL, 0); | |
} | |
private: | |
OSHandleType root_socket; | |
OSHandleType process_handle; | |
// Modules are non-copyable | |
Module() {} | |
Module(const Module&); | |
Module& operator=(const Module&); | |
friend Module* LoadModule(const char* path); | |
}; | |
// Load a NaCl module | |
Module* LoadModule(const char* path); | |
#endif | |
// Module-only definitions | |
#ifdef __native_client__ | |
// Root socket operations, see the descriptions in Module | |
namespace RootSocket { | |
int SendMsg(const void* data, size_t len, IPCHandle* const* handles, size_t num_handles); | |
int RecvMsg(void* buffer, size_t len, IPCHandle** handles, size_t num_handles); | |
inline int SendMsg(const void* data, size_t len) | |
{ | |
return SendMsg(data, len, NULL, 0); | |
} | |
inline int RecvMsg(void* buffer, size_t len) | |
{ | |
return RecvMsg(buffer, len, NULL, 0); | |
} | |
} | |
#endif | |
} // namespace NaClWrapper |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment