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, exit_group test was disabled on Linux-SGX PAL. It was
disabled because the PAL incorrectly exited threads. The latest
commits fix this, and the test can be re-enabled.
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.
Previously, there was a bug in copy of the shim_thread::signal_handles[].action
due to sizeof on the wrong type (shim_signal_handle instead of __kernel_sigaction).
This led to incomplete action object in signal_handles[].
When child thread exits, it wakes up its parent if CLONE_CHILD_CLEARTID was set
during clone() call. Previously, this was done by the child thread itself as
part of its own clean-up in release_clear_child_id(). But this child thread is
still alive at this point and uses some resources, most notably the stack (that
might have been provided by the parent) and the SGX TCS slot. Upon waking up,
the parent might decide to free that stack (as Pthreads do) or re-use the TCS
slot, causing data races.
This commit introduces a correct emulation of CLONE_CHILD_CLEARTID:
- A new argument `PAL_PTR clear_child_tid` is added to DkThreadExit();
it points to internal Graphene memory that is erased on child exit to notify
Async Helper thread.
- At PAL layer, when thread finally exits, it sets PAL-level *clear_child_tid = 0
(corresponds to &clear_child_tid_val_pal at LibOS level); this signals to LibOS
layer that the thread stopped using resources.
- At LibOS layer, Async Helper thread is set up to wait for the signal from
PAL; it is now the responsibility of Async Helper thread to call
release_clear_child_id() to wake up the parent thread.
- Async Helper thread waits for clear_child_tid_val_pal == 0 and then sets
the actual clear_child_tid to 0 and wakes up the waiting parent.
Note that for Linux-SGX PAL, clear_child_tid is set to 0 not immediately
but as part of handle_thread_reset, otherwise the TCS slot could be still
occupied when LibOS wakes up the parent.
As a side effect, the LibOS code for threads/process exit is cleaned up.
This commit also fixes all regression tests to use the new signature of
DkThreadExit() and increases the number of SGX threads slightly (to
accommodate the newly used Async Helper thread).
The LTP test signal06 is a regression test for some obscure bug in older
Linux kernels. It uses SIGHUP and SIGSEGV signals to test mprotect() of
altstack. Since SIGHUP is completely ignored by Graphene, and altstack is
not correctly emulated, and mprotect() doesn't work, this test never made
sense for Graphene. This commit disables it.
These tests perform common FS operations in various ways:
- open/close
- read/write
- create/delete
- read/change size
- seek/tell
- memory-mapped read/write
- copy directory in different ways
Tests use both direct syscalls and stdio wrappers (FILE).
To run, call `make test` in `LibOS/shim/test/fs`.
In Async Helper thread, async_list is protected by async_list_lock.
Previously, this thread unlocked async_list_lock on each detected
triggered entry to perform a callback during traversal of async_list
(unlocking is needed because it is unknown how much time the callback
takes to execute). However, once we unlock, the list may become
unstable (install_async_event may delete/append an entry). Then, next
FOR_EACH_LIST_SAFE list entry may be freed while trigering callback,
and the next iteration may try to dereference this invalid pointer.
This commit alleviates this scenario by creating a temporal list of
triggered entries and performing all callbacks after list traversal.
This patch removes __attribute__((packed)) to eliminate warnings gcc-9
generates. Example:
> warning: taking address of packed member of struct poll_handle may result in an unaligned pointer value [-Waddress-of-packed-member]
Packed attribute is used for structures using which Graphene processes
communicate with each other. We don't implement privilege separation, so
it's ok to leak data in struct paddings.
If the padding is really a concern, we could:
- Define two structures, a packed one for serialization and a non-packed
one for code, and then explicitly (de)serialize.
- Define the structure fields with an explicit size (e.g. uint64_t
instead of long), carefully add explicit padding and then zero it out
before sending.
The academic-quality build script was supplanted by industry-grade
Makefile rule.
Also, glibc 2.19 is not supported, because it is not present in any
supported distro.
The execve() syscall starts a new executable in the *same* process. Previously,
Graphene followed this convention *only* for non-SGX PALs. If PAL was Linux-SGX,
Graphene silently terminated the process and created a new one.
This deviation from standard execve() behavior resulted in the host shell
becoming detached from the Graphene-SGX process. In turn, this led to our
Bash example (on Ubuntu 18.04, bash version 4.4.19) "terminating" early
from the point of view of Jenkins, and SGX-18.04 pipeline failed.
This commit allows Graphene-SGX to execve() in the same process, but only if it
is the same executable (as in the Bash example). It is still impossible to
execve() in the same process for a different executable since this requires a
new SGX enclave measurement and thus demands a new process.
Previously, Graphene libraries were built using `wildcard` make function.
The output of this function (list of files) is non-deterministic (list items
can be in any order). This results in different SGX measurements on rebuilds.
In some build environments (e.g., a dedicated Graphene container), it is
important to keep these measurements deterministic. This commit achieves this
by removing wildcards and explicitly specifying required files.
There are two Python3 examples now:
- python-simple contains simple and secure Python3 scripts;
- python-scipy-insecure contains SciPy and NumPy insecure Python3 scripts.
Both examples are tested in Jenkins.
Previously, struct shim_tcb was embedded in Glibc's TCB but recent
commits to Graphene move shim_tcb to PAL's TCB. Thus, this commit
removes inserting shim_tcb from Glibc patches.
Now, shim_thread::tcb is used only as an integer value for %fs_base,
and shim_thread::user_tcb is not needed anymore. This commit renames
shim_thread::tcb to shim_thread::fs_base and removes user_tcb.
For binaries statically linked against Glibc, %fs register cannot be
used for LibOS TCB (shim_tcb) because it is already used by Glibc.
Thus, this commit moves shim_tcb into PAL TCB. Also, now that LibOS
doesn't access Glibc TCB (__libc_tcb), we make it an opaque pointer
used only for clean up.
- Loop on futex(WAKE) when no thread is awaken.
- Use pthread instead of raw clone() because clone() does not
initialize Glibc TCB, thus any function call to Glibc results
in SIGBUS.