This commit improves the emulation of recvfrom/sendfrom,
recvmsg/sendmsg, and recvmmsg/sendmmsg system calls. In particular,
MSG_DONTWAIT flag is allowed though not really emulated (benign in
most cases). Also, it is possible now to send/receive FDs via
SCM_RIGHTS on a UNIX domain socket (only send/recv of pipes and UNIX
domain sockets is currently supported). Corresponding LibOS test
is added.
This contains two socket-related changes, which would be hard to factor
out into two commits: (because of compilation warnings)
- Making ocalls use size_t in their interface. It's not the same as
Linux syscall interface anyway, so why not make it sane?
- Fixing sycalls arguments types to match the ones use by kernel.
Note: there's no such type as "socklen_t" in the kernel, so it got
removed.
LibOS events create_event() / set_event() / wait_event() are emulated
as reads/writes on a private pipe. On the other hand, PAL API
DkSynchronizationObjectWait() works only on event/mutex objects, not
on pipes. So wait_event(), which previously used this API, failed
on assert because it provided a pipe object. This bug manifests
only in rare circumstances (I found it with Erlang workload) because
wait_event() is called very rarely, on data-race path of epoll wait.
This commit simply removes DkSynchronizationObjectWait() call, so
that the event is awaited via reading from the pipe.
This commit adds support for FIFOs and the corresponding syscalls
mknod() and mknodat(). Internally, FIFOs are emulated as pseudo-
files in chroot mount points (not visible in host FS). FIFOs'
read/write operations are emulated via pipes at PAL level (this
means that they are transparently encrypted under SGX PAL).
Generally, emulation of FIFOs is similar to emulation of named
UNIX domain sockets, i.e., they are "ephemeral" and only allow
communication between two related processes.
New LibOS test is added: multi-process `mkfifo`.
This commit completely reworks VMA subsystem along with its usages.
New version should be: cleaner (easier to maintain), faster and allow
for bookkeeping requests from Pal.
It also fixes some bugs and inconsistencies found in the process and
changes brk and mmap/munmap implementations (at least partially).
GCC 9.3 adds more static checks on C headers and sources. This
commit fixes all detected issues (mainly possible NULL pointer
dereferences and VLAs on stack).
Currently various flags in file and memory syscalls work mostly by an
accident, because values of some of them align with corresponding Linux
syscall flags. Some APIs weren't that lucky though - e.g.
DkStreamOpen(..., /*options=*/PAL_OPTION_CLOEXEC) deletes file contents
(sic!) intead of opening it with O_CLOEXEC. This is because
PAL_OPTION_CLOEXEC == O_TRUNC.
This commit fixes all this mess and also adds asserts to check validity
of flags passed to Dk* handlers.
Current implementation isn't finished, doesn't have a single test and
has quite bad code quality. If we decide we want to implement this
feature, it will be easier to just implement it from scratch.
Previously, Graphene used the notation "pipe:<uint32_t>" to emulate
pipes, socketpairs, and UNIX domain sockets. In particular, pipes
and socketpairs received random integer IDs, and sockets received
deterministic integer IDs. However, 32-bit randomly generated IDs
may collide quite often. Since pipe IDs/names should *not* repeat
(otherwise e.g. derived crypto keys will be reused), this commit
changes pipe IDs (pipeid) from uint32_t to char[96], and pipe IDs
(names) become 256-bit random sequences.
Rework inet_copy_addr by using sockaddr_storage (ported from glibc's
bits/socket.h) for writing the data into, which is large enough to hold
sockaddr_in6. Then copy back into the user's buffer.
Previously some calls to inet_copy_addr were passing in a
'struct sockaddr' that is too small to hold sockaddr_in6 data, thus
causing stack corruption when the socket was an IPv6 socket.
Previously, /proc and /dev pseudo-filesystems were implemented in
completely different ways. This commit introduces a set of generic
functions for all pseudo-FSs (based on previous implementation of
/proc) and refactors both /proc and /dev to use these functions.
This commit also expands a LibOS test `proc` to cover more cases
and pseudo-files, as well as introduces a new LibOS test `dev`.
Several bugs in pseudo-FSs were detected and fixed in the process.
Previously, Linux-SGX logic of allocating/freeing enclave pages was
complicated and hard to read. This commit refactors this code for
readability, without changes in functionality.
Previously, the LibOS logic in chroot FS's read/write emulation tried
to be (too) smart: it mmapped regular files and performed a memcpy on
the user-requested file offset + size. This mmap trick required the
use of DkStreamMap() which is inefficient on Linux-SGX PAL, since for
SGX, the PAL must memcpy from untrusted file buffer into the trusted
enclave buffer. Thus, reads/writes in chroot FS resulted in two
memcpys under SGX.
This commit removes this complicated mmap logic and simply calls
DkStreamRead/Write() for regular files. In other words, the mmap
optimization is moved out of the LibOS layer and into the PAL layer.
For device handles, `info.dev.dev_ops` contains function pointers into
LibOS. They may become invalid due to relocation of LibOS text section
in the child process on fork. This commit forces an update of these
function pointers.
Previously, Graphene failed if recv() contained MSG_PEEK flag. This
resulted in many TLS-based applications failing, including Nginx,
Apache, and Lighttpd in SSL/TLS mode. This commit adds emulation of
MSG_PEEK at LibOS level. A simple TCP test case is provided.
Checkpoint's total memory size is stored in shim_cp_store::mem_size
field. Previously, this field was of type `int`. When a process
allocates more than 2GB of memory and then tries to spawn a child,
the checkpoint send/receive fails due to int overflow of mem_size.
This commit simply changes mem_size type to `size_t`. This is enough
to make the bug go away on e.g. a huge Python app with TensorFlow.
Now Graphene supports an improved version of DkObjectsWaitAny() with
correct polling semantics -- DkObjectsWaitEvents(). This makes
DkObjectsWaitAny() obsolete. This commit removes DkObjectsWaitAny()
and replaces it with:
- DkSynchronizationObjectWait() to wait on a single synchronization
object like mutex or event.
- DkStreamsWaitEvents() to wait on stream-like objects (this is the
renamed DkObjectsWaitEvents()).
The corresponding tests are fixed to use the new PAL interfaces.
Also, IPC helper and Async helper threads are significantly refactored
to make better use of DkStreamsWaitEvents().
This commit improves the emulation of polling mechanisms (select,
pselect, poll, ppoll, epoll_wait) and cleans up the corresponding
code:
- New DkObjectsWaitEvents() PAL interface, replaces the inefficient
DkObjectsWaitAny() interface. This interface closely resembles
Linux/POSIX poll() in semantics.
- Improved shim_do_epoll_wait() implementation, now using the new
DkObjectsWaitEvents() interface.
- Improved shim_do_poll() implementation, now using the new
DkObjectsWaitEvents() interface.
- Small cleanups of polling code.
Accurate cleanup of shim_do_epoll_create1(), shim_do_epoll_ctl(),
shim_do_epoll_wait(), and other epoll helper functions. This cleanup
also adds error handling (missing previously).
The commit makes (most of) the corresponding LTP tests pass now.
Note that epoll semantics are still incorrect and inefficient: current
epoll_wait() emulation returns only one event to the user.
Graphene IPC (GIPC) was introduced to perform faster bulk IPC by sharing pages as copy-on-write
across processes. This feature became stale, and it was shown that recent Linux kernels (4.2+)
have zero-copy transfers over UNIX sockets and exhibit similar performance. GIPC is not built and
not tested in Jenkins. Also, GIPC does not work under SGX. This commit completely removes GIPC.
This commit completely rewrites futex implementation to (hopefully)
remove all races (both on memory access level and between waits and
wakes), make it compatible with actual Linux implementation and make
it more maintainable and readable.
Previously, if user performed getdents() on a directory containing
inaccessible files (because user doesn't have permission), whole
getdents failed with -EACCES. This is incorrect behavior: files must
still be listed. This commit fixes the root cause of this bug by
marking inaccessible files as DENTRY_NEGATIVE.
Previously, there was a data race on thread::is_alive between one thread
checking whether it is the last thread alive via check_last_thread() and
another thread exiting via thread_exit(). The former checks if is_alive
is true, the latter sets it to false. However, the exiting thread will
truly exit only after it called DkThreadExit(), thus the race on is_alive
led to scenarios where two threads believe to be the last threads alive
and compete on terminating Async Helper/IPC threads and exiting the whole
process. This commit introduces cleanup_thread() called by Async Helper
to set is_alive to false and delete the thread, freeing its resources.
The data race is thus removed, and shim_thread object leak is prevented.