Skip to content

Instantly share code, notes, and snippets.

@Amanieu
Last active December 19, 2015 01:09
Show Gist options
  • Save Amanieu/5873672 to your computer and use it in GitHub Desktop.
Save Amanieu/5873672 to your computer and use it in GitHub Desktop.
#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
#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