diff --git a/LibOS/shim/src/bookkeep/shim_thread.c b/LibOS/shim/src/bookkeep/shim_thread.c index 47f8ccb4..50954515 100644 --- a/LibOS/shim/src/bookkeep/shim_thread.c +++ b/LibOS/shim/src/bookkeep/shim_thread.c @@ -34,6 +34,8 @@ #include #include +#include + static IDTYPE tid_alloc_idx __attribute_migratable = 0; static LISTP_TYPE(shim_thread) thread_list = LISTP_INIT; @@ -163,6 +165,8 @@ struct shim_thread * alloc_new_thread (void) INIT_LIST_HEAD(thread, siblings); INIT_LISTP(&thread->exited_children); INIT_LIST_HEAD(thread, list); + /* default value as sigalt stack isn't specified yet */ + thread->signal_altstack.ss_flags = SS_DISABLE; return thread; } diff --git a/LibOS/shim/src/sys/shim_sigaction.c b/LibOS/shim/src/sys/shim_sigaction.c index b7526517..535b379b 100644 --- a/LibOS/shim/src/sys/shim_sigaction.c +++ b/LibOS/shim/src/sys/shim_sigaction.c @@ -160,16 +160,37 @@ int shim_do_sigaltstack (const stack_t * ss, stack_t * oss) struct shim_thread * cur = get_cur_thread(); lock(&cur->lock); + stack_t * cur_ss = &cur->signal_altstack; + if (oss) - *oss = cur->signal_altstack; + *oss = *cur_ss; + + void * sp = shim_get_tls()->context.sp; + /* check if thread is currently executing on an active altstack */ + if (!(cur_ss->ss_flags & SS_DISABLE) && + sp && + cur_ss->ss_sp <= sp && + sp < cur_ss->ss_sp + cur_ss->ss_size) { + if (oss) + oss->ss_flags |= SS_ONSTACK; + if (ss) { + unlock(&cur->lock); + return -EPERM; + } + } if (ss) { - if (ss->ss_size < MINSIGSTKSZ) { - unlock(&cur->lock); - return -ENOMEM; - } + if (ss->ss_flags & SS_DISABLE) { + memset(cur_ss, 0, sizeof(*cur_ss)); + cur_ss->ss_flags = SS_DISABLE; + } else { + if (ss->ss_size < MINSIGSTKSZ) { + unlock(&cur->lock); + return -ENOMEM; + } - cur->signal_altstack = *ss; + *cur_ss = *ss; + } } unlock(&cur->lock); diff --git a/LibOS/shim/test/regression/30_sigaltstack.py b/LibOS/shim/test/regression/30_sigaltstack.py new file mode 100644 index 00000000..d4860b19 --- /dev/null +++ b/LibOS/shim/test/regression/30_sigaltstack.py @@ -0,0 +1,29 @@ +import sys +from regression import Regression + +loader = sys.argv[1] + +# Running sigaltstack +regression = Regression(loader, "sigaltstack") + +messages = ( + "OK on sigaltstack in main thread before alarm", + "&act == 0x", + "sig 14 count 1 goes off with sp=0x", + "OK on signal stack", + "OK on sigaltstack in handler", + "sig 14 count 2 goes off with sp=0x", + "OK on signal stack", + "OK on sigaltstack in handler", + "sig 14 count 3 goes off with sp=0x", + "OK on signal stack", + "OK on sigaltstack in handler", + "OK on sigaltstack in main thread", + "done exiting", +) + +regression.add_check(name="Sigaltstack Test", + check=lambda res: all([x in res[0].out for x in messages])) + +rv = regression.run_checks() +if rv: sys.exit(rv) diff --git a/LibOS/shim/test/regression/Makefile b/LibOS/shim/test/regression/Makefile index de46d1a2..6b16e410 100644 --- a/LibOS/shim/test/regression/Makefile +++ b/LibOS/shim/test/regression/Makefile @@ -52,18 +52,22 @@ ifeq ($(SGX_RUN),1) PYTHONENV += "TIMEOUT=20000" endif +# 30_sigaltstack.py fails because sigaltstack isn't correctly implemented. +BLOCKED_TESTS = \ + 30_sigaltstack.py +get_tests = $(filter-out $(BLOCKED_TESTS),$(wildcard $(1))) .PHONY: regression regression: $(target) @echo "\n\nBasic Bootstrapping:" - @for f in $(wildcard 00_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done + @for f in $(call get_tests,00_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done @echo "\n\nSyscall Support:" - @for f in $(wildcard 30_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done + @for f in $(call get_tests,30_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done @echo "\n\nFile System Support:" - @for f in $(wildcard 40_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done + @for f in $(call get_tests,40_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done @echo "\n\nSocket Support:" - @for f in $(wildcard 80_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done + @for f in $(call get_tests,80_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done @echo "\n\nLarge File Support:" - @for f in $(wildcard 90_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done + @for f in $(call get_tests,90_*.py); do env $(PYTHONENV) python3 $$f $(RUNTIME)/pal-$(PAL_HOST) || exit $$?; done .PHONY: clean-tmp clean-tmp: diff --git a/LibOS/shim/test/regression/sigaltstack.c b/LibOS/shim/test/regression/sigaltstack.c new file mode 100644 index 00000000..3315d2b3 --- /dev/null +++ b/LibOS/shim/test/regression/sigaltstack.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include + +uint8_t* sig_stack; +size_t sig_stack_size = SIGSTKSZ; +_Atomic int count = 0; + +void handler(int signal, siginfo_t* info, void* ucontext) { + int ret; + count++; + + uint8_t a; + printf("sig %d count %d goes off with sp=%p, sig_stack: [%p, %p)\n", signal, count, &a, + sig_stack, sig_stack + sig_stack_size); + if (sig_stack <= &a && &a < sig_stack + sig_stack_size) { + printf("OK on signal stack\n"); + } else { + printf("FAIL out of signal stack\n"); + } + fflush(stdout); + + stack_t old; + memset(&old, 0, sizeof(old)); + ret = sigaltstack(NULL, &old); + if (ret < 0) { + err(EXIT_FAILURE, "sigaltstack in handler"); + } + if (old.ss_flags & SS_ONSTACK) { + printf("OK on sigaltstack in handler\n"); + } else { + printf("FAIL on sigaltstack in handler\n"); + } + + /* + * raise SIGALRM during signal handling to test nested signals + * (three-levels deep nesting just to be sure) + */ + if (count <= 2) { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + ret = sigprocmask(SIG_UNBLOCK, &set, NULL); + if (ret) { + err(EXIT_FAILURE, "sigprocmask"); + } + raise(SIGALRM); + } + count--; +} + +int main(int argc, char** argv) { + int ret; + sig_stack = malloc(sig_stack_size); + if (sig_stack == NULL) { + err(EXIT_FAILURE, "malloc"); + } + + stack_t ss = { + .ss_sp = sig_stack, + .ss_flags = 0, + .ss_size = sig_stack_size, + }; + stack_t old; + memset(&old, 0xff, sizeof(old)); + ret = sigaltstack(&ss, &old); + if (ret < 0) { + err(EXIT_FAILURE, "sigaltstack"); + } + if (old.ss_flags & SS_ONSTACK) { + printf("FAIL on sigaltstack in main thread before alarm\n"); + } else { + printf("OK on sigaltstack in main thread before alarm\n"); + } + + struct sigaction act; + act.sa_sigaction = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; + ret = sigaction(SIGALRM, &act, NULL); + if (ret < 0) { + err(EXIT_FAILURE, "sigaction"); + } + + printf("&act == %p\n", &act); + fflush(stdout); + alarm(1); + pause(); + + memset(&old, 0xff, sizeof(old)); + ret = sigaltstack(NULL, &old); + if (ret < 0) { + err(EXIT_FAILURE, "sigaltstack"); + } + if (old.ss_flags & SS_ONSTACK) { + printf("FAIL on sigaltstack in main thread\n"); + } else { + printf("OK on sigaltstack in main thread\n"); + } + + printf("done exiting\n"); + fflush(stdout); + return 0; +}