Implement Berkeley Socket API for Intel SGX (#1061)

Implement Berkeley Socket API for Intel SGX
- bring Berkeley socket API in Intel SGX enclaves,
- adapt the documentation of the socket API to mention Intel SGX enclaves,
- adapt _iwasm_ in the mini-product _linux-sgx_ to support the same option as the one for _linux_,
- tested on the socket sample as provided by WAMR (the TCP client/server).
This commit is contained in:
Jämes Ménétrey 2022-03-25 10:46:29 +01:00 committed by GitHub
parent 5264ce4118
commit 106974d915
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 543 additions and 38 deletions

View File

@ -9,21 +9,194 @@
#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__)
/** OCALLs prototypes **/
int
ocall_socket(int *p_ret, int domain, int type, int protocol);
ocall_accept(int *p_ret, int sockfd, void *addr, uint32_t *addrlen,
uint32_t addr_size);
int
ocall_bind(int *p_ret, int sockfd, const void *addr, uint32_t addrlen);
int
ocall_close(int *p_ret, int fd);
int
ocall_connect(int *p_ret, int sockfd, void *addr, uint32_t addrlen);
int
ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg);
int
ocall_getsockname(int *p_ret, int sockfd, void *addr, uint32_t *addrlen,
uint32_t addr_size);
int
ocall_getsockopt(int *p_ret, int sockfd, int level, int optname, void *val_buf,
unsigned int val_buf_size, void *len_buf);
int
ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf,
unsigned int msg_buf_size, int flags);
ocall_listen(int *p_ret, int sockfd, int backlog);
int
ocall_recv(int *p_ret, int sockfd, void *buf, size_t len, int flags);
int
ocall_recvmsg(ssize_t *p_ret, int sockfd, void *msg_buf,
unsigned int msg_buf_size, int flags);
int
ocall_send(int *p_ret, int sockfd, const void *buf, size_t len, int flags);
int
ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf,
unsigned int msg_buf_size, int flags);
int
ocall_setsockopt(int *p_ret, int sockfd, int level, int optname, void *optval,
unsigned int optlen);
int
ocall_shutdown(int *p_ret, int sockfd, int how);
int
ocall_socket(int *p_ret, int domain, int type, int protocol);
/** OCALLs prototypes end **/
/** In-enclave implementation of POSIX functions **/
static bool
is_little_endian()
{
long i = 0x01020304;
unsigned char *c = (unsigned char *)&i;
return (*c == 0x04) ? true : false;
}
static void
swap32(uint8 *pData)
{
uint8 value = *pData;
*pData = *(pData + 3);
*(pData + 3) = value;
value = *(pData + 1);
*(pData + 1) = *(pData + 2);
*(pData + 2) = value;
}
static void
swap16(uint8 *pData)
{
uint8 value = *pData;
*(pData) = *(pData + 1);
*(pData + 1) = value;
}
static uint32
htonl(uint32 value)
{
uint32 ret;
if (is_little_endian()) {
ret = value;
swap32((uint8 *)&ret);
return ret;
}
return value;
}
static uint32
ntohl(uint32 value)
{
return htonl(value);
}
static uint16
htons(uint16 value)
{
uint16 ret;
if (is_little_endian()) {
ret = value;
swap16((uint8 *)&ret);
return ret;
}
return value;
}
static uint16
ntohs(uint16 value)
{
return htons(value);
}
/* Coming from musl, under MIT license */
static int
__inet_aton(const char *s0, struct in_addr *dest)
{
const char *s = s0;
unsigned char *d = (void *)dest;
unsigned long a[4] = { 0 };
char *z;
int i;
for (i = 0; i < 4; i++) {
a[i] = strtoul(s, &z, 0);
if (z == s || (*z && *z != '.') || !isdigit(*s))
return 0;
if (!*z)
break;
s = z + 1;
}
if (i == 4)
return 0;
switch (i) {
case 0:
a[1] = a[0] & 0xffffff;
a[0] >>= 24;
case 1:
a[2] = a[1] & 0xffff;
a[1] >>= 16;
case 2:
a[3] = a[2] & 0xff;
a[2] >>= 8;
}
for (i = 0; i < 4; i++) {
if (a[i] > 255)
return 0;
d[i] = a[i];
}
return 1;
}
/* Coming from musl, under MIT license */
static int
inet_addr(const char *p)
{
struct in_addr a;
if (!__inet_aton(p, &a))
return -1;
return a.s_addr;
}
static int
inet_network(const char *p)
{
return ntohl(inet_addr(p));
}
/** In-enclave implementation of POSIX functions end **/
static int
textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out)
{
assert(textual);
out->sin_family = AF_INET;
out->sin_port = htons(port);
out->sin_addr.s_addr = inet_addr(textual);
return BHT_OK;
}
int
socket(int domain, int type, int protocol)
{
@ -232,67 +405,219 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr,
unsigned int *addrlen)
{
errno = ENOSYS;
struct sockaddr addr_tmp;
unsigned int len = sizeof(struct sockaddr);
if (ocall_accept(sock, server_sock, &addr_tmp, &len, len) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (*sock < 0) {
errno = get_errno();
return BHT_ERROR;
}
return BHT_OK;
}
int
os_socket_bind(bh_socket_t socket, const char *host, int *port)
{
errno = ENOSYS;
struct sockaddr_in addr;
struct linger ling;
unsigned int socklen;
int ret;
assert(host);
assert(port);
ling.l_onoff = 1;
ling.l_linger = 0;
if (ocall_fcntl_long(&ret, socket, F_SETFD, FD_CLOEXEC) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret < 0) {
goto fail;
}
if (ocall_setsockopt(&ret, socket, SOL_SOCKET, SO_LINGER, &ling,
sizeof(ling))
!= SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret < 0) {
goto fail;
}
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(*port);
addr.sin_family = AF_INET;
if (ocall_bind(&ret, socket, &addr, sizeof(addr)) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret < 0) {
goto fail;
}
socklen = sizeof(addr);
if (ocall_getsockname(&ret, socket, (void *)&addr, &socklen, socklen)
!= SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret == -1) {
goto fail;
}
*port = ntohs(addr.sin_port);
return BHT_OK;
fail:
errno = get_errno();
return BHT_ERROR;
}
int
os_socket_close(bh_socket_t socket)
{
errno = ENOSYS;
int ret;
if (ocall_close(&ret, socket) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret == -1)
errno = get_errno();
return ret;
}
int
os_socket_connect(bh_socket_t socket, const char *addr, int port)
{}
{
struct sockaddr_in addr_in = { 0 };
socklen_t addr_len = sizeof(struct sockaddr_in);
int ret = 0;
if ((ret = textual_addr_to_sockaddr(addr, port, &addr_in)) < 0) {
return ret;
}
if (ocall_connect(&ret, socket, &addr_in, addr_len) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret == -1)
errno = get_errno();
return ret;
}
int
os_socket_create(bh_socket_t *sock, int tcp_or_udp)
{
errno = ENOSYS;
if (!sock) {
return BHT_ERROR;
}
if (1 == tcp_or_udp) {
if (ocall_socket(sock, AF_INET, SOCK_STREAM, IPPROTO_TCP)
!= SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
}
else if (0 == tcp_or_udp) {
if (ocall_socket(sock, AF_INET, SOCK_DGRAM, 0) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
}
if (*sock == -1) {
errno = get_errno();
return BHT_ERROR;
}
return BHT_OK;
}
int
os_socket_inet_network(const char *cp, uint32 *out)
{
errno = ENOSYS;
return -1;
if (!cp)
return BHT_ERROR;
*out = inet_network(cp);
return BHT_OK;
}
int
os_socket_listen(bh_socket_t socket, int max_client)
{
errno = ENOSYS;
int ret;
if (ocall_listen(&ret, socket, max_client) != SGX_SUCCESS) {
TRACE_OCALL_FAIL();
return -1;
}
if (ret == -1)
errno = get_errno();
return ret;
}
int
os_socket_recv(bh_socket_t socket, void *buf, unsigned int len)
{
int ret;
if (ocall_recv(&ret, socket, buf, len, 0) != SGX_SUCCESS) {
errno = ENOSYS;
return -1;
}
if (ret == -1)
errno = get_errno();
return ret;
}
int
os_socket_send(bh_socket_t socket, const void *buf, unsigned int len)
{
int ret;
if (ocall_send(&ret, socket, buf, len, 0) != SGX_SUCCESS) {
errno = ENOSYS;
return -1;
}
if (ret == -1)
errno = get_errno();
return ret;
}
int
os_socket_shutdown(bh_socket_t socket)
{
errno = ENOSYS;
return -1;
return shutdown(socket, O_RDWR);
}
#endif

View File

@ -14,11 +14,12 @@ extern "C" {
#define SOL_SOCKET 1
#define SO_TYPE 3
#define SO_LINGER 13
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SO_TYPE 3
#define MSG_OOB 0x0001
#define MSG_PEEK 0x0002
#define MSG_DONTROUTE 0x0004
@ -44,6 +45,16 @@ extern "C" {
#define SHUT_WR 1
#define SHUT_RDWR 2
/* Address families. */
#define AF_INET 2 /* IP protocol family. */
/* Standard well-defined IP protocols. */
#define IPPROTO_TCP 6 /* Transmission Control Protocol. */
/* Types of sockets. */
#define SOCK_DGRAM \
2 /* Connectionless, unreliable datagrams of fixed maximum length. */
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
@ -54,6 +65,36 @@ struct msghdr {
int msg_flags;
};
/* Internet address. */
struct in_addr {
uint32_t s_addr;
};
typedef struct in_addr in_addr_t;
/* Structure describing an Internet socket address. */
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
uint16_t sin_family;
uint16_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char__pad[__SOCK_SIZE__ - sizeof(uint16_t) - sizeof(uint16_t)
- sizeof(struct in_addr)];
};
/* Structure used to manipulate the SO_LINGER option. */
struct linger {
int l_onoff; /* Nonzero to linger on close. */
int l_linger; /* Time to linger. */
};
/* Structure describing a generic socket address. */
struct sockaddr {
unsigned short int sa_family; /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
int
socket(int domain, int type, int protocol);

View File

@ -118,19 +118,34 @@ enclave {
int ocall_pthread_rwlock_unlock([user_check]void *rwlock);
int ocall_get_errno();
int ocall_socket(int domain, int type, int protocol);
/* sockets */
int ocall_accept(int sockfd, [in, size=addr_size]void *addr,
[in, size=4] uint32_t *addrlen, uint32_t addr_size);
int ocall_bind(int sockfd, [in, size=addrlen]const void *addr,
uint32_t addrlen);
int ocall_connect(int sockfd, [in, size=addrlen]void *addr, uint32_t addrlen);
int ocall_getsockname(int sockfd, [in, size=addr_size]void *addr,
[in, out, size=4]uint32_t *addrlen, uint32_t addr_size);
int ocall_getsockopt(int sockfd, int level, int optname,
[out, size=val_buf_size]void *val_buf,
unsigned int val_buf_size,
[in, out, size=4]void *len_buf);
ssize_t ocall_sendmsg(int sockfd,
[in, size=msg_buf_size]void *msg_buf,
unsigned int msg_buf_size,
int flags);
int ocall_listen(int sockfd, int backlog);
int ocall_recv(int sockfd, [out, size=len]void *buf, size_t len, int flags);
ssize_t ocall_recvmsg(int sockfd,
[in, out, size=msg_buf_size]void *msg_buf,
unsigned int msg_buf_size,
int flags);
int ocall_send(int sockfd, [in, size=len]const void *buf, size_t len, int flags);
ssize_t ocall_sendmsg(int sockfd,
[in, size=msg_buf_size]void *msg_buf,
unsigned int msg_buf_size,
int flags);
int ocall_setsockopt(int sockfd, int level, int optname,
[in, size=optlen]void *optval,
unsigned int optlen);
int ocall_shutdown(int sockfd, int how);
int ocall_socket(int domain, int type, int protocol);
};
};

View File

@ -6,6 +6,8 @@
#include <sys/socket.h>
#include <stdint.h>
#include <stddef.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int
ocall_socket(int domain, int type, int protocol)
@ -73,3 +75,52 @@ ocall_shutdown(int sockfd, int how)
{
return shutdown(sockfd, how);
}
int
ocall_setsockopt(int sockfd, int level, int optname, void *optval,
unsigned int optlen)
{
return setsockopt(sockfd, level, optname, optval, optlen);
}
int
ocall_bind(int sockfd, const void *addr, uint32_t addrlen)
{
return bind(sockfd, (const struct sockaddr *)addr, addrlen);
}
int
ocall_getsockname(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size)
{
return getsockname(sockfd, (struct sockaddr *)addr, addrlen);
}
int
ocall_listen(int sockfd, int backlog)
{
return listen(sockfd, backlog);
}
int
ocall_accept(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size)
{
return accept(sockfd, (struct sockaddr *)addr, addrlen);
}
int
ocall_recv(int sockfd, void *buf, size_t len, int flags)
{
return recv(sockfd, buf, len, flags);
}
int
ocall_send(int sockfd, const void *buf, size_t len, int flags)
{
return send(sockfd, buf, len, flags);
}
int
ocall_connect(int sockfd, void *addr, uint32_t addrlen)
{
return connect(sockfd, (const struct sockaddr *)addr, addrlen);
}

View File

@ -10,7 +10,7 @@ and `socket()`. Users can call those functions in WebAssembly code directly.
Those WebAssembly socket calls will be dispatched to the imported
functions and eventually will be implemented by host socket APIs.
This document introduces a way to support _Berkeley/Posix Socket APIs_ in
This document introduces a way to support the _Berkeley/POSIX Socket API_ in
WebAssembly code.
## Patch the native code
@ -24,7 +24,7 @@ native source code.
#endif
```
`__wasi__` is a Marco defined by WASI. The host compiler will not enable it.
`__wasi__` is a macro defined by WASI. The host compiler will not enable it.
## CMake files
@ -57,10 +57,25 @@ The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1`. By default, it is
enabled.
_iwasm_ accepts address ranges via an option, `--addr-pool`, to implement
the capability control. All IP address the WebAssebmly application may need to `bind()` or `connect()` should be announced first. Every IP address should be in CIRD notation.
the capability control. All IP address the WebAssembly application may need to `bind()` or `connect()`
should be announced first. Every IP address should be in CIRD notation.
```bash
$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm
```
Refer to [socket api sample](../samples/socket-api) for more details.
## Intel SGX support
WAMR also supports the socket API within Intel SGX enclaves.
The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1` and `WAMR_BUILD_LIB_PTHREAD=1`, which are enabled by default.
Similarly to running _iwasm_ outside of an enclave, the allowed address ranges are given via the option `--addr-pool`.
```bash
$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm
```
Refer to [socket api sample](../samples/socket-api) for the compilation of the Wasm applications and [_iwasm_ for Intel SGX](../product-mini/platforms/linux-sgx) for the Wasm runtime.

View File

@ -227,6 +227,10 @@ print_help()
printf(" --dir=<dir> Grant wasi access to the given host directories\n");
printf(" to the program, for example:\n");
printf(" --dir=<dir1> --dir=<dir2>\n");
printf(" --addr-pool= Grant wasi access to the given network addresses in\n");
printf(" CIRD notation to the program, seperated with ',',\n");
printf(" for example:\n");
printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n");
printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n");
return 1;
}
@ -550,9 +554,10 @@ app_instance_func(void *wasm_module_inst, const char *func_name, int app_argc,
static bool
set_wasi_args(void *wasm_module, const char **dir_list, uint32_t dir_list_size,
const char **env_list, uint32_t env_list_size, int stdinfd,
int stdoutfd, int stderrfd, char **argv, uint32_t argc)
int stdoutfd, int stderrfd, char **argv, uint32_t argc,
const char **addr_pool, uint32_t addr_pool_size)
{
uint64_t ecall_args[10];
uint64_t ecall_args[12];
ecall_args[0] = (uint64_t)(uintptr_t)wasm_module;
ecall_args[1] = (uint64_t)(uintptr_t)dir_list;
@ -564,9 +569,11 @@ set_wasi_args(void *wasm_module, const char **dir_list, uint32_t dir_list_size,
ecall_args[7] = stderrfd;
ecall_args[8] = (uint64_t)(uintptr_t)argv;
ecall_args[9] = argc;
ecall_args[10] = (uint64_t)(uintptr_t)addr_pool;
ecall_args[11] = addr_pool_size;
if (SGX_SUCCESS
!= ecall_handle_command(g_eid, CMD_SET_WASI_ARGS, (uint8_t *)ecall_args,
sizeof(uint64_t) * 10)) {
sizeof(uint64_t) * 12)) {
printf("Call ecall_handle_command() failed.\n");
}
@ -590,6 +597,8 @@ main(int argc, char *argv[])
uint32_t dir_list_size = 0;
const char *env_list[8] = { NULL };
uint32_t env_list_size = 0;
const char *addr_pool[8] = { NULL };
uint32_t addr_pool_size = 0;
uint32_t max_thread_num = 4;
if (enclave_init(&g_eid) < 0) {
@ -666,6 +675,26 @@ main(int argc, char *argv[])
return print_help();
}
}
/* TODO: parse the configuration file via --addr-pool-file */
else if (!strncmp(argv[0], "--addr-pool=", strlen("--addr-pool="))) {
/* like: --addr-pool=100.200.244.255/30 */
char *token = NULL;
if ('\0' == argv[0][12])
return print_help();
token = strtok(argv[0] + strlen("--addr-pool="), ",");
while (token) {
if (addr_pool_size >= sizeof(addr_pool) / sizeof(char *)) {
printf("Only allow max address number %d\n",
(int)(sizeof(addr_pool) / sizeof(char *)));
return -1;
}
addr_pool[addr_pool_size++] = token;
token = strtok(NULL, ";");
}
}
else if (!strncmp(argv[0], "--max-threads=", 14)) {
if (argv[0][14] == '\0')
return print_help();
@ -705,7 +734,8 @@ main(int argc, char *argv[])
/* Set wasi arguments */
if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list,
env_list_size, 0, 1, 2, argv, argc)) {
env_list_size, 0, 1, 2, argv, argc, addr_pool,
addr_pool_size)) {
printf("%s\n", "set wasi arguments failed.\n");
goto fail3;
}
@ -771,6 +801,8 @@ wamr_pal_create_process(struct wamr_pal_create_process_args *args)
uint32_t dir_list_size = 0;
const char *env_list[8] = { NULL };
uint32_t env_list_size = 0;
const char *addr_pool[8] = { NULL };
uint32_t addr_pool_size = 0;
uint32_t max_thread_num = 4;
char *wasm_files[16];
void *wasm_module_inst[16];
@ -845,7 +877,7 @@ wamr_pal_create_process(struct wamr_pal_create_process_args *args)
/* Set wasi arguments */
if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list,
env_list_size, stdinfd, stdoutfd, stderrfd, argv,
argc)) {
argc, addr_pool, addr_pool_size)) {
printf("%s\n", "set wasi arguments failed.\n");
unload_module(wasm_module);
free(wasm_file_buf);

View File

@ -50,6 +50,8 @@ typedef struct EnclaveModule {
uint32 wasi_dir_list_size;
char **wasi_env_list;
uint32 wasi_env_list_size;
char **wasi_addr_pool_list;
uint32 wasi_addr_pool_list_size;
char **wasi_argv;
uint32 wasi_argc;
bool is_xip_file;
@ -407,6 +409,8 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
char **wasi_argv = *(char ***)args++;
char *p, *p1;
uint32 wasi_argc = *(uint32 *)args++;
char **addr_pool_list = *(char ***)args++;
uint32 addr_pool_list_size = *(uint32 *)args++;
uint64 total_size = 0;
int32 i, str_len;
@ -414,6 +418,7 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
total_size += sizeof(char *) * (uint64)dir_list_size
+ sizeof(char *) * (uint64)env_list_size
+ sizeof(char *) * (uint64)addr_pool_list_size
+ sizeof(char *) * (uint64)wasi_argc;
for (i = 0; i < dir_list_size; i++) {
@ -424,6 +429,10 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
total_size += strlen(env_list[i]) + 1;
}
for (i = 0; i < addr_pool_list_size; i++) {
total_size += strlen(addr_pool_list[i]) + 1;
}
for (i = 0; i < wasi_argc; i++) {
total_size += strlen(wasi_argv[i]) + 1;
}
@ -436,7 +445,7 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
}
p1 = p + sizeof(char *) * dir_list_size + sizeof(char *) * env_list_size
+ sizeof(char *) * wasi_argc;
+ sizeof(char *) * addr_pool_list_size + sizeof(char *) * wasi_argc;
if (dir_list_size > 0) {
enclave_module->wasi_dir_list = (char **)p;
@ -462,6 +471,18 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
p += sizeof(char *) * env_list_size;
}
if (addr_pool_list_size > 0) {
enclave_module->wasi_addr_pool_list = (char **)p;
enclave_module->wasi_addr_pool_list_size = addr_pool_list_size;
for (i = 0; i < addr_pool_list_size; i++) {
enclave_module->wasi_addr_pool_list[i] = p1;
str_len = strlen(addr_pool_list[i]);
bh_memcpy_s(p1, str_len + 1, addr_pool_list[i], str_len + 1);
p1 += str_len + 1;
}
p += sizeof(char *) * addr_pool_list_size;
}
if (wasi_argc > 0) {
enclave_module->wasi_argv = (char **)p;
enclave_module->wasi_argc = wasi_argc;
@ -481,6 +502,11 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc)
(stdinfd != -1) ? stdinfd : 0, (stdoutfd != -1) ? stdoutfd : 1,
(stderrfd != -1) ? stderrfd : 2);
wasm_runtime_set_wasi_addr_pool(
enclave_module->module,
(const char **)enclave_module->wasi_addr_pool_list,
addr_pool_list_size);
*args_org = true;
}
#else