From 3ee41aa9fff69d71ae61ef4ba1dbba85bfedcd72 Mon Sep 17 00:00:00 2001 From: "Krishnakumar, Sudha" Date: Mon, 14 Oct 2019 13:10:38 -0700 Subject: [PATCH] [LibOS, Pal] Support eventfd() This commit adds support for eventfd(): - shim_do_eventfd() and shim_do_eventfd2() emulation at LibOS level; - new eventfd pseudo-FS; - db_eventfd.c emulation to route eventfd calls to host OS at Pal level (implementation for Linux and Linux-SGX, stubs for Skeleton); - new OCALL ocall_eventfd() for Linux-SGX Pal; - LibOS regression test for eventfd. This implementation of eventfd() correctly handles IPC between threads of the same process; it also must handle IPC between parent/ child processes. This implementation currently doesn't support the scenario when kernel signals the process via eventfd, but is easily extensible for this. The implementation currently doesn't have additional checks to prevent Iago attacks on eventfd. --- LibOS/shim/include/shim_fs.h | 1 + LibOS/shim/include/shim_handle.h | 1 + LibOS/shim/include/shim_table.h | 6 +- LibOS/shim/src/Makefile | 2 +- LibOS/shim/src/fs/eventfd/fs.c | 110 +++++++++++++ LibOS/shim/src/fs/shim_fs.c | 3 +- LibOS/shim/src/shim_syscalls.c | 6 +- LibOS/shim/src/sys/shim_eventfd.c | 89 +++++++++++ LibOS/shim/test/regression/Makefile | 1 + LibOS/shim/test/regression/eventfd.c | 163 ++++++++++++++++++++ LibOS/shim/test/regression/test_libos.py | 7 + Pal/src/db_streams.c | 4 + Pal/src/host/FreeBSD/pal_host.h | 2 +- Pal/src/host/Linux-SGX/Makefile | 2 +- Pal/src/host/Linux-SGX/db_eventfd.c | 187 +++++++++++++++++++++++ Pal/src/host/Linux-SGX/enclave_ocalls.c | 20 +++ Pal/src/host/Linux-SGX/enclave_ocalls.h | 2 + Pal/src/host/Linux-SGX/ocall_types.h | 6 + Pal/src/host/Linux-SGX/pal_host.h | 7 +- Pal/src/host/Linux-SGX/sgx_enclave.c | 14 +- Pal/src/host/Linux/Makefile | 2 +- Pal/src/host/Linux/db_eventfd.c | 186 ++++++++++++++++++++++ Pal/src/host/Linux/pal_host.h | 8 +- Pal/src/host/Skeleton/Makefile | 2 +- Pal/src/host/Skeleton/db_eventfd.c | 63 ++++++++ Pal/src/host/Skeleton/pal_host.h | 6 +- Pal/src/pal.h | 13 +- 27 files changed, 895 insertions(+), 18 deletions(-) create mode 100644 LibOS/shim/src/fs/eventfd/fs.c create mode 100644 LibOS/shim/src/sys/shim_eventfd.c create mode 100644 LibOS/shim/test/regression/eventfd.c create mode 100644 Pal/src/host/Linux-SGX/db_eventfd.c create mode 100644 Pal/src/host/Linux/db_eventfd.c create mode 100644 Pal/src/host/Skeleton/db_eventfd.c diff --git a/LibOS/shim/include/shim_fs.h b/LibOS/shim/include/shim_fs.h index 1e1cf12c..23006d0d 100644 --- a/LibOS/shim/include/shim_fs.h +++ b/LibOS/shim/include/shim_fs.h @@ -569,6 +569,7 @@ extern struct shim_mount chroot_builtin_fs; extern struct shim_mount pipe_builtin_fs; extern struct shim_mount socket_builtin_fs; extern struct shim_mount epoll_builtin_fs; +extern struct shim_mount eventfd_builtin_fs; /* proc file system */ struct proc_nm_ops { diff --git a/LibOS/shim/include/shim_handle.h b/LibOS/shim/include/shim_handle.h index b573f4ba..993d09e0 100644 --- a/LibOS/shim/include/shim_handle.h +++ b/LibOS/shim/include/shim_handle.h @@ -50,6 +50,7 @@ enum shim_handle_type { TYPE_FUTEX, TYPE_STR, TYPE_EPOLL, + TYPE_EVENTFD }; struct shim_handle; diff --git a/LibOS/shim/include/shim_table.h b/LibOS/shim/include/shim_table.h index 3dde77cc..44f69004 100644 --- a/LibOS/shim/include/shim_table.h +++ b/LibOS/shim/include/shim_table.h @@ -480,6 +480,8 @@ ssize_t shim_do_recvmmsg(int sockfd, struct mmsghdr* msg, size_t vlen, int flags int shim_do_prlimit64(pid_t pid, int resource, const struct __kernel_rlimit64* new_rlim, struct __kernel_rlimit64* old_rlim); ssize_t shim_do_sendmmsg(int sockfd, struct mmsghdr* msg, size_t vlen, int flags); +int shim_do_eventfd2(unsigned int count, int flags); +int shim_do_eventfd(unsigned int count); /* libos call implementation */ int shim_do_msgpersist(int msqid, int cmd); @@ -781,14 +783,14 @@ int shim_epoll_pwait(int epfd, struct __kernel_epoll_event* events, int maxevent const __sigset_t* sigmask, size_t sigsetsize); int shim_signalfd(int ufd, __sigset_t* user_mask, size_t sizemask); int shim_timerfd_create(int clockid, int flags); -int shim_eventfd(int count); +int shim_eventfd(unsigned int count); int shim_fallocate(int fd, int mode, loff_t offset, loff_t len); int shim_timerfd_settime(int ufd, int flags, const struct __kernel_itimerspec* utmr, struct __kernel_itimerspec* otmr); int shim_timerfd_gettime(int ufd, struct __kernel_itimerspec* otmr); int shim_accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, int flags); int shim_signalfd4(int ufd, __sigset_t* user_mask, size_t sizemask, int flags); -int shim_eventfd2(int count, int flags); +int shim_eventfd2(unsigned int count, int flags); int shim_epoll_create1(int flags); int shim_dup3(int oldfd, int newfd, int flags); int shim_pipe2(int* fildes, int flags); diff --git a/LibOS/shim/src/Makefile b/LibOS/shim/src/Makefile index ee04fdc9..bd9429f4 100644 --- a/LibOS/shim/src/Makefile +++ b/LibOS/shim/src/Makefile @@ -39,7 +39,7 @@ files_to_install = $(addprefix $(RUNTIME_DIR)/,$(files_to_build)) defs = -DIN_SHIM CFLAGS += $(defs) ASFLAGS += $(defs) -fs = chroot str pipe socket proc dev +fs = chroot str pipe socket proc dev eventfd ipcns = pid sysv objs = $(addprefix bookkeep/shim_,handle vma thread signal) \ $(patsubst %.c,%,$(wildcard utils/*.c)) \ diff --git a/LibOS/shim/src/fs/eventfd/fs.c b/LibOS/shim/src/fs/eventfd/fs.c new file mode 100644 index 00000000..2a5bad98 --- /dev/null +++ b/LibOS/shim/src/fs/eventfd/fs.c @@ -0,0 +1,110 @@ +/* Copyright (C) 2019 Intel Corporation + This file is part of Graphene Library OS. + + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* + * fs.c + * + * This file contains codes for implementation of 'eventfd' filesystem. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ssize_t eventfd_read(struct shim_handle* hdl, void* buf, size_t count) { + if (!count) + return 0; + + if (count < sizeof(uint64_t)) + return -EINVAL; + + PAL_NUM bytes = DkStreamRead(hdl->pal_handle, 0, count, buf, NULL, 0); + + if (bytes <= 0) + return -PAL_ERRNO; + + assert((ssize_t ) bytes == sizeof(uint64_t)); + + return (ssize_t) bytes; +} + +static ssize_t eventfd_write(struct shim_handle* hdl, const void* buf, size_t count) { + if (!count) + return 0; + + if (count < sizeof(uint64_t)) + return -EINVAL; + + PAL_NUM bytes = DkStreamWrite(hdl->pal_handle, 0, count, (void *) buf, NULL); + + if (bytes <= 0) + return -PAL_ERRNO; + + assert((ssize_t ) bytes == sizeof(uint64_t)); + + return (ssize_t) bytes; +} + +static off_t eventfd_poll(struct shim_handle* hdl, int poll_type) { + off_t ret = 0; + + lock(&hdl->lock); + + if (!hdl->pal_handle) { + ret = -EBADF; + goto out; + } + + PAL_STREAM_ATTR attr; + if (!DkStreamAttributesQueryByHandle(hdl->pal_handle, &attr)) { + ret = -PAL_ERRNO; + goto out; + } + + if (poll_type == FS_POLL_SZ) { + ret = attr.pending_size; + goto out; + } + + ret = 0; + if (attr.disconnected) + ret |= FS_POLL_ER; + if ((poll_type & FS_POLL_RD) && attr.readable) + ret |= FS_POLL_RD; + if ((poll_type & FS_POLL_WR) && attr.writable) + ret |= FS_POLL_WR; + +out: + unlock(&hdl->lock); + return ret; +} + +struct shim_fs_ops eventfd_fs_ops = { + .read = &eventfd_read, + .write = &eventfd_write, + .poll = &eventfd_poll, +}; + +struct shim_mount eventfd_builtin_fs = { + .type = "eventfd", + .fs_ops = &eventfd_fs_ops, +}; diff --git a/LibOS/shim/src/fs/shim_fs.c b/LibOS/shim/src/fs/shim_fs.c index b83e7cb5..a9623277 100644 --- a/LibOS/shim/src/fs/shim_fs.c +++ b/LibOS/shim/src/fs/shim_fs.c @@ -46,13 +46,14 @@ struct shim_fs mountable_fs [NUM_MOUNTABLE_FS] = { { .name = "dev", .fs_ops = &dev_fs_ops, .d_ops = &dev_d_ops, }, }; -#define NUM_BUILTIN_FS 4 +#define NUM_BUILTIN_FS 5 struct shim_mount * builtin_fs [NUM_BUILTIN_FS] = { &chroot_builtin_fs, &pipe_builtin_fs, &socket_builtin_fs, &epoll_builtin_fs, + &eventfd_builtin_fs, }; static struct shim_lock mount_mgr_lock; diff --git a/LibOS/shim/src/shim_syscalls.c b/LibOS/shim/src/shim_syscalls.c index 9cd12730..378b3df8 100644 --- a/LibOS/shim/src/shim_syscalls.c +++ b/LibOS/shim/src/shim_syscalls.c @@ -963,8 +963,6 @@ SHIM_SYSCALL_PASSTHROUGH(signalfd, 3, int, int, ufd, __sigset_t*, user_mask, siz SHIM_SYSCALL_PASSTHROUGH(timerfd_create, 2, int, int, clockid, int, flags) -SHIM_SYSCALL_PASSTHROUGH(eventfd, 1, int, int, count) - SHIM_SYSCALL_PASSTHROUGH(fallocate, 4, int, int, fd, int, mode, loff_t, offset, loff_t, len) SHIM_SYSCALL_PASSTHROUGH(timerfd_settime, 4, int, int, ufd, int, flags, @@ -979,7 +977,9 @@ DEFINE_SHIM_SYSCALL(accept4, 4, shim_do_accept4, int, int, sockfd, struct sockad SHIM_SYSCALL_PASSTHROUGH(signalfd4, 4, int, int, ufd, __sigset_t*, user_mask, size_t, sizemask, int, flags) -SHIM_SYSCALL_PASSTHROUGH(eventfd2, 2, int, int, count, int, flags) +DEFINE_SHIM_SYSCALL(eventfd, 1, shim_do_eventfd, int, unsigned int, count) + +DEFINE_SHIM_SYSCALL (eventfd2, 2, shim_do_eventfd2, int, unsigned int, count, int, flags) /* epoll_create1: sys/shim_epoll.c */ DEFINE_SHIM_SYSCALL(epoll_create1, 1, shim_do_epoll_create1, int, int, flags) diff --git a/LibOS/shim/src/sys/shim_eventfd.c b/LibOS/shim/src/sys/shim_eventfd.c new file mode 100644 index 00000000..1eff8615 --- /dev/null +++ b/LibOS/shim/src/sys/shim_eventfd.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2019 Intel Corporation + This file is part of Graphene Library OS. + + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* + * shim_eventfd.c + * + * Implementation of system calls "eventfd" and "eventfd2". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int create_eventfd(PAL_HANDLE* efd, unsigned count, int flags) { + PAL_HANDLE hdl = NULL; + int pal_flags = 0; + + pal_flags |= flags & EFD_NONBLOCK ? PAL_OPTION_NONBLOCK : 0; + pal_flags |= flags & EFD_CLOEXEC ? PAL_OPTION_CLOEXEC : 0; + pal_flags |= flags & EFD_SEMAPHORE ? PAL_OPTION_EFD_SEMAPHORE : 0; + + /* eventfd() requires count (aka initval) but PAL's DkStreamOpen() doesn't have such an argument. + * Using create arg as a work-around (note: initval is uint32 but create is int32).*/ + if (!(hdl = DkStreamOpen("eventfd:", 0, 0, count, pal_flags))) { + debug("eventfd open failure\n"); + return -PAL_ERRNO; + } + + *efd = hdl; + return 0; + +} + +int shim_do_eventfd2(unsigned int count, int flags) { + int ret = 0; + struct shim_handle* hdl = get_new_handle(); + + if (!hdl) { + ret = -ENOMEM; + goto out; + } + + hdl->type = TYPE_EVENTFD; + set_handle_fs(hdl, &eventfd_builtin_fs); + hdl->flags = O_RDWR; + hdl->acc_mode = MAY_READ | MAY_WRITE; + + if ((ret = create_eventfd(&hdl->pal_handle, count, flags)) < 0) + goto out; + + flags = flags & EFD_CLOEXEC ? FD_CLOEXEC : 0; + + /* get_new_handle() above increments hdl's refcount. + * Followed by another increment inside set_new_fd_handle. + * So we need to put_handle() afterwards. */ + int vfd = set_new_fd_handle(hdl, flags, NULL); + + ret = vfd; + +out: + if (hdl) + put_handle(hdl); + + return ret; + +} + +int shim_do_eventfd(unsigned int count) { + return shim_do_eventfd2(count, 0); +} diff --git a/LibOS/shim/test/regression/Makefile b/LibOS/shim/test/regression/Makefile index d8ff6b84..fa6fd72c 100644 --- a/LibOS/shim/test/regression/Makefile +++ b/LibOS/shim/test/regression/Makefile @@ -22,6 +22,7 @@ CFLAGS-openmp = -fopenmp CFLAGS-multi_pthread = -pthread CFLAGS-exit_group = -pthread CFLAGS-abort_multithread = -pthread +CFLAGS-eventfd = -pthread %: %.c $(call cmd,csingle) diff --git a/LibOS/shim/test/regression/eventfd.c b/LibOS/shim/test/regression/eventfd.c new file mode 100644 index 00000000..d3a8f691 --- /dev/null +++ b/LibOS/shim/test/regression/eventfd.c @@ -0,0 +1,163 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_EFDS 3 + +int efds[MAX_EFDS] = { 0 }; + +void* write_eventfd_thread(void* arg) { + uint64_t count = 10; + + int* efds = (int*) arg; + + if (!arg) { + printf("arg is NULL\n"); + return NULL; + } + + printf("%s:got here\n", __func__); + + for (int i = 0; i < MAX_EFDS; i++) { + printf("%s: efd = %d\n", __func__, efds[i]); + } + + for (int i = 0; i < MAX_EFDS; i++) { + sleep(1); + write(efds[i], &count, sizeof(count)); + count += 1; + } + + return NULL; +} + +/* This function used to test polling on a group of eventfd descriptors. + * To support regression testing, positive value returned for error case. */ +int eventfd_using_poll() { + int ret = 0; + struct pollfd pollfds[MAX_EFDS]; + pthread_t tid = 0; + uint64_t count = 0; + int poll_ret = 0; + int nread_events = 0; + + for (int i = 0; i < MAX_EFDS; i++) { + efds[i] = eventfd(0, 0); + + if (efds[i] < 0) { + perror("eventfd failed"); + return 1; + } + + printf("efd = %d\n", efds[i]); + + pollfds[i].fd = efds[i]; + pollfds[i].events = POLLIN; + } + + ret = pthread_create(&tid, NULL, write_eventfd_thread, efds); + + if (ret != 0) { + perror("error in thread creation\n"); + return 1; + } + + while (1) { + poll_ret = poll(pollfds, MAX_EFDS, 5000); + + if (poll_ret == 0) { + printf("Poll timed out. Exiting.\n"); + break; + } + + if (poll_ret < 0) { + perror("error from poll"); + ret = 1; + break; + } + + for (int i = 0; i < MAX_EFDS; i++) { + if (pollfds[i].revents & POLLIN) { + pollfds[i].revents = 0; + errno = 0; + read(pollfds[i].fd, &count, sizeof(count)); + printf("fd set=%d\n", pollfds[i].fd); + printf("efd = %d, count: %lu, errno=%d\n", pollfds[i].fd, + count, errno); + nread_events++; + } + } + } + + if (nread_events == MAX_EFDS) { + printf("%s completed successfully\n", __func__); + } else + printf("%s: nread_events=%d, MAX_EFDS=%d\n", __func__, nread_events, MAX_EFDS); + + pthread_join(tid, NULL); + return ret; +} + +/* This function used to test various flags supported while creating eventfd descriptors. + * Note: EFD_SEMAPHORE has not been tested. + * To support regression testing, positive value returned for error case. */ +int eventfd_using_various_flags() { + uint64_t count = 0; + int efd = 0; + int eventfd_flags[] = { 0, EFD_NONBLOCK, EFD_CLOEXEC, EFD_NONBLOCK | EFD_CLOEXEC }; + + for (int i = 0; i < sizeof(eventfd_flags) / sizeof(int); i++) { + printf("iteration #-%d, flags=%d\n", i, eventfd_flags[i]); + + efd = eventfd(0, eventfd_flags[i]); + + if (efd < 0) { + perror("eventfd failed"); + printf("eventfd error for iteration #-%d, flags-%d\n", i, eventfd_flags[i]); + return 1; + } + + count = 5; + eventfd_write(efd, count); + eventfd_write(efd, count); + count = 0; + errno = 0; + eventfd_read(efd, &count); + printf("efd = %d, count: %lu, errno=%d\n", efd, count, errno); + + /* calling the second read would block if flags doesn't have EFD_NONBLOCK */ + if (eventfd_flags[i] & EFD_NONBLOCK) { + count = 0; + errno = 0; + eventfd_read(efd, &count); + printf("efd = %d, count: %lu, errno=%d\n", efd, count, errno); + } + + close(efd); + } + + printf("%s completed successfully\n", __func__); + + return 0; +} + +int main(int argc, char* argv[]) { + int ret = 0; + + ret = eventfd_using_poll(); + ret += eventfd_using_various_flags(); + + return ret; +} diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py index cf175b62..d197b09a 100644 --- a/LibOS/shim/test/regression/test_libos.py +++ b/LibOS/shim/test/regression/test_libos.py @@ -311,6 +311,13 @@ class TC_30_Syscall(RegressionTestCase): self.assertIn('OK on sigaltstack in main thread', stdout) self.assertIn('done exiting', stdout) + def test_070_eventfd(self): + stdout, stderr = self.run_binary(['eventfd']) + + # Eventfd Test + self.assertIn('eventfd_using_poll completed successfully', stdout) + self.assertIn('eventfd_using_various_flags completed successfully', stdout) + @unittest.skipUnless(HAS_SGX, 'This test is only meaningful on SGX PAL because only SGX catches raw ' 'syscalls and redirects to Graphene\'s LibOS. If we will add seccomp to ' diff --git a/Pal/src/db_streams.c b/Pal/src/db_streams.c index 35a44ea0..39be6735 100644 --- a/Pal/src/db_streams.c +++ b/Pal/src/db_streams.c @@ -45,6 +45,7 @@ extern struct handle_ops mutex_ops; extern struct handle_ops event_ops; extern struct handle_ops gipc_ops; extern struct handle_ops mcast_ops; +extern struct handle_ops eventfd_ops; const struct handle_ops* pal_handle_ops[PAL_HANDLE_TYPE_BOUND] = { [pal_type_file] = &file_ops, @@ -64,6 +65,7 @@ const struct handle_ops* pal_handle_ops[PAL_HANDLE_TYPE_BOUND] = { [pal_type_mutex] = &mutex_ops, [pal_type_event] = &event_ops, [pal_type_gipc] = &gipc_ops, + [pal_type_eventfd] = &eventfd_ops, }; /* parse_stream_uri scan the uri, seperate prefix and search for @@ -106,6 +108,8 @@ static int parse_stream_uri(const char** uri, char** prefix, struct handle_ops** hops = &tcp_ops; else if (strstartswith_static(u, "udp.srv")) hops = &udp_ops; + else if (strstartswith_static(u, "eventfd")) + hops = &eventfd_ops; break; case 8: diff --git a/Pal/src/host/FreeBSD/pal_host.h b/Pal/src/host/FreeBSD/pal_host.h index ed4689b8..c254f599 100644 --- a/Pal/src/host/FreeBSD/pal_host.h +++ b/Pal/src/host/FreeBSD/pal_host.h @@ -47,7 +47,7 @@ typedef struct mutex_handle { #define _DkInternalLock _DkMutexLock #define _DkInternalUnlock _DkMutexUnlock -#define MAX_FDS 3 + typedef union pal_handle { /* TSAI: Here we define the internal types of PAL_HANDLE diff --git a/Pal/src/host/Linux-SGX/Makefile b/Pal/src/host/Linux-SGX/Makefile index 9bfcbafd..66d1e01e 100644 --- a/Pal/src/host/Linux-SGX/Makefile +++ b/Pal/src/host/Linux-SGX/Makefile @@ -12,7 +12,7 @@ host_files = libpal-Linux-SGX.a pal-sgx debugger/sgx_gdb.so pal.map generated_of defs = -DIN_PAL -DPAL_DIR=$(PAL_DIR) -DRUNTIME_DIR=$(RUNTIME_DIR) CFLAGS += $(defs) ASFLAGS += $(defs) -enclave-objs = $(addprefix db_,files devices pipes sockets streams memory \ +enclave-objs = $(addprefix db_,files devices pipes eventfd sockets streams memory \ threading mutex events process object main rtld \ exception misc ipc spinlock) \ $(addprefix enclave_,ocalls ecalls framework platform pages untrusted) diff --git a/Pal/src/host/Linux-SGX/db_eventfd.c b/Pal/src/host/Linux-SGX/db_eventfd.c new file mode 100644 index 00000000..a7ababe2 --- /dev/null +++ b/Pal/src/host/Linux-SGX/db_eventfd.c @@ -0,0 +1,187 @@ +/* Copyright (C) 2019 Intel Corporation + This file is part of Graphene Library OS. + + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* + * db_eventfd.c + * + * This file contains operations to handle streams with URIs that have + * "eventfd:". + */ + +#include +#include +#include +#include +#include + +#include "api.h" +#include "pal_defs.h" +#include "pal_linux_defs.h" +#include "pal.h" +#include "pal_internal.h" +#include "pal_linux.h" +#include "pal_linux_error.h" +#include "pal_error.h" +#include "pal_security.h" +#include "pal_debug.h" + +static inline int eventfd_type(int options) { + int type = 0; + if (options & PAL_OPTION_NONBLOCK) + type |= EFD_NONBLOCK; + + if (options & PAL_OPTION_CLOEXEC) + type |= EFD_CLOEXEC; + + if (options & PAL_OPTION_EFD_SEMAPHORE) + type |= EFD_SEMAPHORE; + + return type; +} + +/* `type` must be eventfd, `uri` & `access` & `share` are unused, + * `create` holds eventfd's initval, `options` holds eventfd's flags */ +static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access, + int share, int create, int options) { + int ret; + __UNUSED(access); + __UNUSED(share); + + if ((strcmp_static(type, "eventfd") != 0) || (*uri != '\0')) { + return -PAL_ERROR_INVAL; + } + + /* Using create arg as a work-around (note: initval is uint32 but create is int32).*/ + ret = ocall_eventfd(create, eventfd_type(options)); + + if (IS_ERR(ret)) + return unix_to_pal_error(ERRNO(ret)); + + PAL_HANDLE hdl = malloc(HANDLE_SIZE(eventfd)); + SET_HANDLE_TYPE(hdl, eventfd); + + /* Note: using index 0, given that there is only 1 eventfd FD per pal-handle. */ + HANDLE_HDR(hdl)->flags = RFD(0) | WFD(0) | WRITABLE(0); + + hdl->eventfd.fd = ret; + hdl->eventfd.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE; + *handle = hdl; + + return 0; + +} + +static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) { + if (offset) + return -PAL_ERROR_INVAL; + + if (!IS_HANDLE_TYPE(handle, eventfd)) + return -PAL_ERROR_NOTCONNECTION; + + if (len < sizeof(uint64_t)) + return -PAL_ERROR_INVAL; + + /* TODO: verify that the value returned in buffer is somehow meaningful + * (to prevent Iago attacks) */ + int bytes = ocall_read(handle->eventfd.fd, buffer, len); + + if (IS_ERR(bytes)) + return unix_to_pal_error(ERRNO(bytes)); + + if (!bytes) + return -PAL_ERROR_ENDOFSTREAM; + + return bytes; +} + +static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, + const void* buffer) { + if (offset) + return -PAL_ERROR_INVAL; + + if (!IS_HANDLE_TYPE(handle, eventfd)) + return -PAL_ERROR_NOTCONNECTION; + + if (len < sizeof(uint64_t)) + return -PAL_ERROR_INVAL; + + int bytes = ocall_write(handle->eventfd.fd, buffer, len); + PAL_FLG writable = WRITABLE(0); + + if (IS_ERR(bytes)) { + if (ERRNO(bytes) == EAGAIN) + HANDLE_HDR(handle)->flags &= ~writable; + return unix_to_pal_error(ERRNO(bytes)); + } + + /* whether fd is writable or not, gets updated here, + * to optimize polling logic in _DkObjectsWaitAny */ + if ((uint64_t) bytes == sizeof(uint64_t)) + HANDLE_HDR(handle)->flags |= writable; + else + HANDLE_HDR(handle)->flags &= ~writable; + + return bytes; +} + +/* invoked during poll operation on eventfd from LibOS. */ +static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) { + if (handle->generic.fds[0] == PAL_IDX_POISON) + return -PAL_ERROR_BADHANDLE; + + attr->handle_type = PAL_GET_TYPE(handle); + + int efd = handle->eventfd.fd; + int flags = HANDLE_HDR(handle)->flags; + + struct pollfd pfd = { .fd = efd, .events = POLLIN, .revents = 0 }; + int ret = ocall_poll(&pfd, 1, 0); + + if (IS_ERR(ret)) + return unix_to_pal_error(ERRNO(ret)); + + attr->readable = (ret == 1 && pfd.revents == POLLIN); + attr->disconnected = flags & ERROR(0); + attr->nonblocking = handle->eventfd.nonblocking; + + /* For future use, so that Linux host kernel can send notifications to user-space apps. + * App receives virtual FD from LibOS, but the Linux-host eventfd is memorized + * here, such that this Linux-host eventfd can be retrieved (by LibOS) during app's ioctl(). */ + attr->no_of_fds = 1; + attr->fds[0] = efd; + + return 0; +} + +static int eventfd_pal_close(PAL_HANDLE handle) { + if (IS_HANDLE_TYPE(handle, eventfd)) { + if (handle->eventfd.fd != PAL_IDX_POISON) { + ocall_close(handle->eventfd.fd); + handle->eventfd.fd = PAL_IDX_POISON; + } + return 0; + } + + return 0; +} + +struct handle_ops eventfd_ops = { + .open = &eventfd_pal_open, + .read = &eventfd_pal_read, + .write = &eventfd_pal_write, + .close = &eventfd_pal_close, + .attrquerybyhdl = &eventfd_pal_attrquerybyhdl, +}; diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.c b/Pal/src/host/Linux-SGX/enclave_ocalls.c index ddd0666e..6e85b8ec 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.c +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.c @@ -1243,3 +1243,23 @@ reset: out: return retval; } + +int ocall_eventfd (unsigned int initval, int flags) +{ + int retval = 0; + ms_ocall_eventfd_t * ms; + + ms = sgx_alloc_on_ustack(sizeof(*ms)); + if (!ms) { + sgx_reset_ustack(); + return -EPERM; + } + + ms->ms_initval = initval; + ms->ms_flags = flags; + + retval = sgx_ocall(OCALL_EVENTFD, ms); + + sgx_reset_ustack(); + return retval; +} diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.h b/Pal/src/host/Linux-SGX/enclave_ocalls.h index 7729e765..67df43ca 100644 --- a/Pal/src/host/Linux-SGX/enclave_ocalls.h +++ b/Pal/src/host/Linux-SGX/enclave_ocalls.h @@ -104,3 +104,5 @@ int ocall_load_debug (const char * command); int ocall_get_attestation(const sgx_spid_t* spid, const char* subkey, bool linkable, const sgx_report_t* report, const sgx_quote_nonce_t* nonce, sgx_attestation_t* attestation); +int ocall_eventfd (unsigned int initval, int flags); + diff --git a/Pal/src/host/Linux-SGX/ocall_types.h b/Pal/src/host/Linux-SGX/ocall_types.h index 88cbfa4f..c1b62c86 100644 --- a/Pal/src/host/Linux-SGX/ocall_types.h +++ b/Pal/src/host/Linux-SGX/ocall_types.h @@ -59,6 +59,7 @@ enum { OCALL_DELETE, OCALL_LOAD_DEBUG, OCALL_GET_ATTESTATION, + OCALL_EVENTFD, OCALL_NR, }; @@ -286,4 +287,9 @@ typedef struct { sgx_attestation_t ms_attestation; } ms_ocall_get_attestation_t; +typedef struct { + unsigned int ms_initval; + int ms_flags; +} ms_ocall_eventfd_t; + #pragma pack(pop) diff --git a/Pal/src/host/Linux-SGX/pal_host.h b/Pal/src/host/Linux-SGX/pal_host.h index 2bcb708a..e14139e7 100644 --- a/Pal/src/host/Linux-SGX/pal_host.h +++ b/Pal/src/host/Linux-SGX/pal_host.h @@ -40,7 +40,6 @@ int _DkSpinUnlock (struct spinlock * lock); #define LOCK_INIT { .value = { 0 } } #define _DkInternalLock _DkSpinLock #define _DkInternalUnlock _DkSpinUnlock -#define MAX_FDS 3 void * malloc_untrusted (int size); void free_untrusted (void * mem); @@ -113,6 +112,12 @@ typedef struct pal_handle PAL_BOL nonblocking; } pipeprv; + struct { + PAL_IDX fd; + /* TODO: add other flags in future, if needed (e.g., semaphore) */ + PAL_BOL nonblocking; + } eventfd; + struct { PAL_IDX fd_in, fd_out; PAL_IDX dev_type; diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c index e2b2b3c9..fa98b029 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.c +++ b/Pal/src/host/Linux-SGX/sgx_enclave.c @@ -670,6 +670,17 @@ static int sgx_ocall_delete(void * pms) return ret; } +static int sgx_ocall_eventfd (void * pms) +{ + ms_ocall_eventfd_t * ms = (ms_ocall_eventfd_t *) pms; + int ret; + ODEBUG(OCALL_EVENTFD, ms); + + ret = INLINE_SYSCALL(eventfd2, 2, ms->ms_initval, ms->ms_flags); + + return ret; +} + void load_gdb_command (const char * command); static int sgx_ocall_load_debug(void * pms) @@ -727,7 +738,8 @@ sgx_ocall_fn_t ocall_table[OCALL_NR] = { [OCALL_DELETE] = sgx_ocall_delete, [OCALL_LOAD_DEBUG] = sgx_ocall_load_debug, [OCALL_GET_ATTESTATION] = sgx_ocall_get_attestation, - }; + [OCALL_EVENTFD] = sgx_ocall_eventfd, +}; #define EDEBUG(code, ms) do {} while (0) diff --git a/Pal/src/host/Linux/Makefile b/Pal/src/host/Linux/Makefile index 9cf7ec84..1e6e73b7 100644 --- a/Pal/src/host/Linux/Makefile +++ b/Pal/src/host/Linux/Makefile @@ -15,7 +15,7 @@ host_files = libpal-Linux.a pal.map defs = -DIN_PAL -DPAL_DIR=$(PAL_DIR) -DRUNTIME_DIR=$(RUNTIME_DIR) CFLAGS += $(defs) ASFLAGS += $(defs) -objs = $(addprefix db_,files devices pipes sockets streams memory threading \ +objs = $(addprefix db_,files devices pipes eventfd sockets streams memory threading \ mutex events process object main rtld misc ipc \ exception) clone-x86_64 graphene_lib = .lib/graphene-lib.a diff --git a/Pal/src/host/Linux/db_eventfd.c b/Pal/src/host/Linux/db_eventfd.c new file mode 100644 index 00000000..314578a5 --- /dev/null +++ b/Pal/src/host/Linux/db_eventfd.c @@ -0,0 +1,186 @@ +/* Copyright (C) 2019 Intel Corporation + This file is part of Graphene Library OS. + + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* + * db_eventfd.c + * + * This file contains operations to handle streams with URIs that have + * "eventfd:". + */ + +#include +#include +#include +#include +#include +#include + +#include "api.h" +#include "pal_defs.h" +#include "pal_linux_defs.h" +#include "pal.h" +#include "pal_internal.h" +#include "pal_linux.h" +#include "pal_linux_error.h" +#include "pal_error.h" +#include "pal_security.h" +#include "pal_debug.h" + +static inline int eventfd_type(int options) { + int type = 0; + if (options & PAL_OPTION_NONBLOCK) + type |= EFD_NONBLOCK; + + if (options & PAL_OPTION_CLOEXEC) + type |= EFD_CLOEXEC; + + if (options & PAL_OPTION_EFD_SEMAPHORE) + type |= EFD_SEMAPHORE; + + return type; +} + +/* `type` must be eventfd, `uri` & `access` & `share` are unused, + * `create` holds eventfd's initval, `options` holds eventfd's flags */ +static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access, + int share, int create, int options) { + int ret; + + __UNUSED(access); + __UNUSED(share); + + if ((strcmp_static(type, "eventfd") != 0) || (*uri != '\0')) { + return -PAL_ERROR_INVAL; + } + + /* Using create arg as a work-around (note: initval is uint32 but create is int32).*/ + ret = INLINE_SYSCALL(eventfd2, 2, create, eventfd_type(options)); + + if (IS_ERR(ret)) + return unix_to_pal_error(ERRNO(ret)); + + PAL_HANDLE hdl = malloc(HANDLE_SIZE(eventfd)); + SET_HANDLE_TYPE(hdl, eventfd); + + /* Note: using index 0, given that there is only 1 eventfd FD per pal-handle. */ + HANDLE_HDR(hdl)->flags = RFD(0) | WFD(0) | WRITABLE(0); + + hdl->eventfd.fd = ret; + hdl->eventfd.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE; + *handle = hdl; + + return 0; + +} + +static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) { + if (offset) + return -PAL_ERROR_INVAL; + + if (!IS_HANDLE_TYPE(handle, eventfd)) + return -PAL_ERROR_NOTCONNECTION; + + if (len < sizeof(uint64_t)) + return -PAL_ERROR_INVAL; + + int bytes = INLINE_SYSCALL(read, 3, handle->eventfd.fd, buffer, len); + + if (IS_ERR(bytes)) + return unix_to_pal_error(ERRNO(bytes)); + + if (!bytes) + return -PAL_ERROR_ENDOFSTREAM; + + return bytes; +} + +static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, + const void* buffer) { + if (offset) + return -PAL_ERROR_INVAL; + + if (!IS_HANDLE_TYPE(handle, eventfd)) + return -PAL_ERROR_NOTCONNECTION; + + if (len < sizeof(uint64_t)) + return -PAL_ERROR_INVAL; + + int bytes = INLINE_SYSCALL(write, 3, handle->eventfd.fd, buffer, len); + PAL_FLG writable = WRITABLE(0); + + if (IS_ERR(bytes)) { + if (ERRNO(bytes) == EAGAIN) + HANDLE_HDR(handle)->flags &= ~writable; + return unix_to_pal_error(ERRNO(bytes)); + } + + /* whether fd is writable or not, gets updated here, + * to optimize polling logic in _DkObjectsWaitAny */ + if ((uint64_t) bytes == sizeof(uint64_t)) + HANDLE_HDR(handle)->flags |= writable; + else + HANDLE_HDR(handle)->flags &= ~writable; + + return bytes; +} + +/* invoked during poll operation on eventfd from LibOS. */ +static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) { + if (handle->generic.fds[0] == PAL_IDX_POISON) + return -PAL_ERROR_BADHANDLE; + + attr->handle_type = PAL_GET_TYPE(handle); + int efd = handle->eventfd.fd; + int flags = HANDLE_HDR(handle)->flags; + + struct pollfd pfd = { .fd = efd, .events = POLLIN, .revents = 0 }; + struct timespec tp = {0, 0}; + int ret = INLINE_SYSCALL(ppoll, 5, &pfd, 1, &tp, NULL, 0); + + if (IS_ERR(ret)) + return unix_to_pal_error(ERRNO(ret)); + + attr->readable = (ret == 1 && pfd.revents == POLLIN); + attr->disconnected = flags & ERROR(0); + attr->nonblocking = handle->eventfd.nonblocking; + + /* For future use, so that Linux host kernel can send notifications to user-space apps. + * App receives virtual FD from LibOS, but the Linux-host eventfd is memorized + * here, such that this Linux-host eventfd can be retrieved (by LibOS) during app's ioctl(). */ + attr->no_of_fds = 1; + attr->fds[0] = efd; + + return 0; +} + +static int eventfd_pal_close(PAL_HANDLE handle) { + if (IS_HANDLE_TYPE(handle, eventfd)) { + if (handle->eventfd.fd != PAL_IDX_POISON) { + INLINE_SYSCALL(close, 1, handle->eventfd.fd); + handle->eventfd.fd = PAL_IDX_POISON; + } + } + + return 0; +} + +struct handle_ops eventfd_ops = { + .open = &eventfd_pal_open, + .read = &eventfd_pal_read, + .write = &eventfd_pal_write, + .close = &eventfd_pal_close, + .attrquerybyhdl = &eventfd_pal_attrquerybyhdl, +}; diff --git a/Pal/src/host/Linux/pal_host.h b/Pal/src/host/Linux/pal_host.h index 093b873d..d594fd95 100644 --- a/Pal/src/host/Linux/pal_host.h +++ b/Pal/src/host/Linux/pal_host.h @@ -67,8 +67,6 @@ typedef struct { #endif } PAL_RESERVED_HDR; -#define MAX_FDS 3 - typedef struct pal_handle { /* TSAI: Here we define the internal types of PAL_HANDLE @@ -106,6 +104,12 @@ typedef struct pal_handle PAL_BOL nonblocking; } pipeprv; + struct { + PAL_IDX fd; + /* TODO: add other flags in future, if needed (e.g., semaphore) */ + PAL_BOL nonblocking; + } eventfd; + struct { PAL_IDX fd_in, fd_out; PAL_IDX dev_type; diff --git a/Pal/src/host/Skeleton/Makefile b/Pal/src/host/Skeleton/Makefile index c453fae6..6d07a269 100644 --- a/Pal/src/host/Skeleton/Makefile +++ b/Pal/src/host/Skeleton/Makefile @@ -9,7 +9,7 @@ host_files = libpal-Skeleton.a pal.map defs = -DIN_PAL CFLAGS += $(defs) ASFLAGS += $(defs) -objs = $(addprefix db_,files devices pipes sockets streams memory threading \ +objs = $(addprefix db_,files devices pipes eventfd sockets streams memory threading \ mutex events process object main rtld misc ipc exception) headers = $(wildcard *.h) $(wildcard ../../*.h) $(wildcard ../../../lib/*.h) diff --git a/Pal/src/host/Skeleton/db_eventfd.c b/Pal/src/host/Skeleton/db_eventfd.c new file mode 100644 index 00000000..cf9bac7b --- /dev/null +++ b/Pal/src/host/Skeleton/db_eventfd.c @@ -0,0 +1,63 @@ +/* Copyright (C) 2019 Intel Corporation + This file is part of Graphene Library OS. + + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* + * db_eventfd.c + * + * This file contains operations to handle streams with URIs that have + * "eventfd:". + */ + +#include "pal_defs.h" +#include "pal.h" +#include "pal_internal.h" +#include "pal_error.h" +#include "api.h" + +/* `type` must be eventfd, `uri` & `access` & `share` are unused, + * `create` holds eventfd's initval, `options` holds eventfd's flags */ +static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access, + int share, int create, int options) { + return -PAL_ERROR_NOTIMPLEMENTED; +} + +/* offset does not apply here. */ +static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) { + return -PAL_ERROR_NOTIMPLEMENTED; +} + +/* offset does not apply here. */ +static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len, + const void* buffer) { + return -PAL_ERROR_NOTIMPLEMENTED; +} + +/* gets used for polling(query) on eventfd from LibOS. */ +static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) { + return -PAL_ERROR_NOTIMPLEMENTED; +} + +static int eventfd_pal_close(PAL_HANDLE handle) { + return -PAL_ERROR_NOTIMPLEMENTED; +} + +struct handle_ops eventfd_ops = { + .open = &eventfd_pal_open, + .read = &eventfd_pal_read, + .write = &eventfd_pal_write, + .close = &eventfd_pal_close, + .attrquerybyhdl = &eventfd_pal_attrquerybyhdl, +}; diff --git a/Pal/src/host/Skeleton/pal_host.h b/Pal/src/host/Skeleton/pal_host.h index cebacd4f..c9efa2fb 100644 --- a/Pal/src/host/Skeleton/pal_host.h +++ b/Pal/src/host/Skeleton/pal_host.h @@ -34,8 +34,6 @@ typedef struct mutex_handle { #define LOCK_INIT {} #define INIT_LOCK(lock) do {} while (0) -#define MAX_FDS 3 - typedef struct pal_handle { /* TSAI: Here we define the internal types of PAL_HANDLE @@ -66,6 +64,10 @@ typedef struct pal_handle PAL_IDX fd; } pipeprv; + struct { + PAL_IDX unused; + } eventfd; + struct { PAL_IDX fd; PAL_IDX dev_type; diff --git a/Pal/src/pal.h b/Pal/src/pal.h index 529d3f37..1dff9bac 100644 --- a/Pal/src/pal.h +++ b/Pal/src/pal.h @@ -35,6 +35,10 @@ typedef uint32_t PAL_FLG; typedef uint32_t PAL_IDX; typedef bool PAL_BOL; +/* Moved MAX_FDS from /pal_host.h to here, + * since it is 3, across all host kernels. */ +#define MAX_FDS 3 + #ifdef IN_PAL #include typedef struct atomic_int PAL_REF; @@ -142,10 +146,10 @@ enum { pal_type_mutex, pal_type_event, pal_type_gipc, + pal_type_eventfd, PAL_HANDLE_TYPE_BOUND, }; - #define PAL_IDX_POISON ((PAL_IDX)-1) /* PAL identifier poison value */ #define PAL_GET_TYPE(h) (HANDLE_HDR(h)->type) #define PAL_CHECK_TYPE(h, t) (PAL_GET_TYPE(h) == pal_type_##t) @@ -324,6 +328,11 @@ DkProcessExit (PAL_NUM exitCode); #define PAL_OPTION_NONBLOCK 04000 #define PAL_OPTION_MASK 04000 +/* CLOEXEC is generic for any stream. + * SEMAPHORE is specific to eventfd syscall. */ +#define PAL_OPTION_CLOEXEC 01000 +#define PAL_OPTION_EFD_SEMAPHORE 02000 + #define WITHIN_MASK(val, mask) (((val)|(mask)) == (mask)) PAL_HANDLE @@ -377,6 +386,8 @@ typedef struct { PAL_BOL readable, writable, runnable; PAL_FLG share_flags; PAL_NUM pending_size; + PAL_IDX no_of_fds; + PAL_IDX fds[MAX_FDS]; union { struct { PAL_NUM linger;