Browse Source

[LibOS] Add logic for SS_DISABLE and SS_ONSTACK in sigaltstack()

This commit also adds LibOS regression test for sigaltstack(). This
test is currently disabled (signal delivery on sigaltstack is not yet
implemented).
Isaku Yamahata 5 years ago
parent
commit
c4bcf4ae8c

+ 4 - 0
LibOS/shim/src/bookkeep/shim_thread.c

@@ -34,6 +34,8 @@
 #include <pal.h>
 #include <list.h>
 
+#include <linux/signal.h>
+
 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;
 }
 

+ 27 - 6
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);
 
-    if (oss)
-        *oss = cur->signal_altstack;
+    stack_t * cur_ss = &cur->signal_altstack;
 
-    if (ss) {
-        if (ss->ss_size < MINSIGSTKSZ) {
+    if (oss)
+        *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 -ENOMEM;
+            return -EPERM;
         }
+    }
 
-        cur->signal_altstack = *ss;
+    if (ss) {
+        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_ss = *ss;
+        }
     }
 
     unlock(&cur->lock);

+ 29 - 0
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)

+ 9 - 5
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:

+ 108 - 0
LibOS/shim/test/regression/sigaltstack.c

@@ -0,0 +1,108 @@
+#include <err.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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;
+}