Before this commit, events like signals and exit_group are handled by LibOS threads in a cooperative fashion: if the user code executed by a LibOS thread does not invoke system calls (e.g., a busy loop), then the LibOS won't have any opportunity to take control and handle events. With the help from the POSIX signal-based interrupt mechanism of Occlum's version of Intel SGX SDK, the LibOS can now interrupt the execution of arbitrary user code in a LibOS thread by sending real-time POSIX signals (the signal number is 64) to it. These signals are sent by a helper thread spawn by Occlum PAL. The helper thread periodically enters into the enclave to check if there are any LibOS threads with pending events. If any, the helper thread broadcast POSIX signals to them. When interrupted by a signal, the receiver LibOS thread may be in one of the two previously problematic states in terms of event handling: 1. Executing non-cooperative user code (e.g., a busy loop). In this case, the signal will trigger an interrupt handler inside the enclave, which can then enter the LibOS kernel to deal with any pending events. 2. Executing an OCall that invokes blocking system calls (e.g., futex, nanosleep, or blocking I/O). In this case, the signal will interrupt the blocking system call so that the OCall can return back to the enclave. Thanks to the new interrupt subsystem, some event-based system calls are made robust. One such example is exit_group. We can now guarantee that exit_group can force any thread in a process to exit.
79 lines
2.4 KiB
C
79 lines
2.4 KiB
C
#define _GNU_SOURCE
|
|
#include <sys/types.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <linux/futex.h>
|
|
#include "test.h"
|
|
|
|
// ============================================================================
|
|
// Test case
|
|
// ============================================================================
|
|
|
|
//
|
|
// Three types of threads that will not exit voluntarily
|
|
//
|
|
|
|
// Type 1: a busy loop thread
|
|
static void *busyloop_thread_func(void *_) {
|
|
while (1) { }
|
|
return NULL;
|
|
}
|
|
|
|
// Type 2: a sleeping thread
|
|
static void *sleeping_thread_func(void *_) {
|
|
unsigned int a_year_in_sec = 365 * 24 * 60 * 60;
|
|
sleep(a_year_in_sec);
|
|
return NULL;
|
|
}
|
|
|
|
// Type 3: a thead that waits on a futex FOREVER
|
|
static void *futex_wait_thread_func(void *_) {
|
|
int my_private_futex = 0;
|
|
syscall(SYS_futex, &my_private_futex, FUTEX_WAIT, my_private_futex, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
// exit_group syscall should terminate all threads in a thread group.
|
|
int test_exit_group_to_force_threads_terminate(void) {
|
|
// Create three types of threads that will not exit voluntarily
|
|
pthread_t busyloop_thread;
|
|
if (pthread_create(&busyloop_thread, NULL, busyloop_thread_func, NULL) < 0) {
|
|
printf("ERROR: pthread_create failed\n");
|
|
return -1;
|
|
}
|
|
pthread_t sleeping_thread;
|
|
if (pthread_create(&sleeping_thread, NULL, sleeping_thread_func, NULL) < 0) {
|
|
printf("ERROR: pthread_create failed\n");
|
|
return -1;
|
|
}
|
|
pthread_t futex_wait_thread;
|
|
if (pthread_create(&futex_wait_thread, NULL, futex_wait_thread_func, NULL) < 0) {
|
|
printf("ERROR: pthread_create failed\n");
|
|
return -1;
|
|
}
|
|
|
|
// Sleep for a while to make sure all three threads are running
|
|
useconds_t half_second = 500 * 1000; // in us
|
|
usleep(half_second);
|
|
|
|
// exit_group syscall will be called eventually by libc's exit, after the
|
|
// main function returns. If Occlum can terminate normally, this means
|
|
// exit_group syscall taking effect.
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Test suite
|
|
// ============================================================================
|
|
|
|
static test_case_t test_cases[] = {
|
|
TEST_CASE(test_exit_group_to_force_threads_terminate)
|
|
};
|
|
|
|
int main() {
|
|
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
|
}
|