From a798f70284b1940c2dcf14f673e8926b8b761160 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Thu, 18 Jul 2024 15:56:15 +0200 Subject: [PATCH] libc: add limited sigaltstack support Allocate a Genode known stack via alloc_secondary_stack and register it as alternative stack via Signal:use_alternative_stack(). The original semantic of Posix, where the caller may choose arbitary stack pointers is currently not possible. Warn about the fact. Issue #5305 --- repos/libports/src/lib/libc/dummies.cc | 1 - repos/libports/src/lib/libc/internal/signal.h | 31 +++++-- repos/libports/src/lib/libc/signal.cc | 37 ++++++++ repos/libports/src/test/libc/main.cc | 93 +++++++++++++++++++ 4 files changed, 151 insertions(+), 11 deletions(-) diff --git a/repos/libports/src/lib/libc/dummies.cc b/repos/libports/src/lib/libc/dummies.cc index 5188dca88b..8d3606d5ac 100644 --- a/repos/libports/src/lib/libc/dummies.cc +++ b/repos/libports/src/lib/libc/dummies.cc @@ -136,7 +136,6 @@ DUMMY(int , -1, sched_setparam, (pid_t, const sched_param *)) DUMMY(int , -1, sched_setscheduler, (pid_t, int, const sched_param *)) DUMMY(int , -1, sched_yield, (void)) DUMMY(int , -1, __semctl, (void)) -DUMMY_SILENT(int , -1, sigaltstack, (const stack_t *, stack_t *)) DUMMY(int , -1, setegid, (uid_t)) DUMMY(int , -1, seteuid, (uid_t)) DUMMY(int , -1, setgid, (gid_t)) diff --git a/repos/libports/src/lib/libc/internal/signal.h b/repos/libports/src/lib/libc/internal/signal.h index d3cd395f65..e3675f9259 100644 --- a/repos/libports/src/lib/libc/internal/signal.h +++ b/repos/libports/src/lib/libc/internal/signal.h @@ -61,8 +61,9 @@ struct Libc::Signal : Noncopyable pid_t const _local_pid; - void * _signal_stack { }; - jmp_buf _signal_context { }; + void * _signal_stack_default { }; + void * _signal_stack_alternative { }; + jmp_buf _signal_context { }; struct Signal_arguments { @@ -83,24 +84,31 @@ struct Libc::Signal : Noncopyable void _execute_on_signal_stack(Pending &pending) { - if (!_signal_stack) { + bool const onstack = signal_action[pending.n].sa_flags & SA_ONSTACK; + + void * signal_stack = (_signal_stack_alternative && onstack) ? + _signal_stack_alternative : + _signal_stack_default; + + if (!signal_stack) { auto myself = Thread::myself(); if (myself) - _signal_stack = { myself->alloc_secondary_stack("signal", 16 * 1024) }; + _signal_stack_default = { myself->alloc_secondary_stack("signal", 16 * 1024) }; + + signal_stack = _signal_stack_default; } - if (!_signal_stack) { - error("signal stack allocation failed"); + if (!signal_stack) { + error(__func__, " signal stack allocation failed"); return; } + Signal_arguments const arg(*this, pending); + /* save continuation of current stack */ if (!_setjmp(_signal_context)) { - - Signal_arguments arg(*this, pending); - /* _setjmp() returned directly -> switch to signal stack */ - call_func(_signal_stack, (void *)_signal_entry, (void *)&arg); + call_func(signal_stack, (void *)_signal_entry, (void *)&arg); /* never reached */ } @@ -120,6 +128,9 @@ struct Libc::Signal : Noncopyable _count++; } + void use_alternative_stack(void *ptr) { + _signal_stack_alternative = ptr; } + void execute_signal_handlers() { /* diff --git a/repos/libports/src/lib/libc/signal.cc b/repos/libports/src/lib/libc/signal.cc index cf9740a096..570c36db3d 100644 --- a/repos/libports/src/lib/libc/signal.cc +++ b/repos/libports/src/lib/libc/signal.cc @@ -229,3 +229,40 @@ extern "C" int raise(int sig) return Libc::Errno(EINVAL); }; } + + +extern "C" int sigaltstack(stack_t const * const ss, stack_t * const old_ss) +{ + if (!_signal_ptr) + return Errno(EINVAL); + + auto &signal = *_signal_ptr; + + if (ss) { + auto myself = Thread::myself(); + if (!myself) + return Errno(EINVAL); + + if (ss->ss_flags & SS_DISABLE) { + /* on disable use the default signal stack */ + signal.use_alternative_stack(nullptr); + + warning("leaking secondary stack memory"); + + } else { + if (ss->ss_sp) + warning(__func__, " using self chosen stack is not" + " supported - stack ptr is ignored !!!"); + + void * stack = myself->alloc_secondary_stack("sigaltstack", + ss->ss_size); + + signal.use_alternative_stack(stack); + } + } + + if (old_ss && ss && !(ss->ss_flags & SS_DISABLE)) + old_ss->ss_flags = SS_DISABLE; + + return 0; +} diff --git a/repos/libports/src/test/libc/main.cc b/repos/libports/src/test/libc/main.cc index 716daa4819..ba5c9f3560 100644 --- a/repos/libports/src/test/libc/main.cc +++ b/repos/libports/src/test/libc/main.cc @@ -32,8 +32,14 @@ extern "C" { #include #include #include + +#include +#include +#include } +static void test_sigalt(); + int main(int argc, char **argv) { printf("--- libC test ---\n"); @@ -262,5 +268,92 @@ int main(int argc, char **argv) puts("Check mktime: success"); } while (0); + test_sigalt(); + exit(error_count); } + + +static struct State { + sigjmp_buf reenter; + volatile sig_atomic_t called; +} thread_state; + + +static void test_signal_handler(int const signal) +{ + thread_state.called = 1; + + void * var = nullptr; + printf("%s stack=%p\n", __func__, &var); + + if (!sigsetjmp(thread_state.reenter, 0)) { + /* do something useful here */ + + /* finally jump back */ + siglongjmp(thread_state.reenter, 1); + } + + printf("%s done\n", __func__); +} + + +static void test_sigalt() +{ + struct sigaction sa { }; + struct sigaction sa_old { }; + stack_t ss { }; + stack_t ss_old { }; + sigset_t sigs { }; + + printf("%s stack=%p\n", __func__, &sa); + + sa.sa_handler = test_signal_handler; + sa.sa_flags = SA_ONSTACK; + sigfillset(&sa.sa_mask); + + if (sigaction(SIGUSR2, &sa, &sa_old) != 0) { + abort(); + } + + /** + * Here the contrib code provides the self allocated stack pointer, + * which we can not support by now. Ported software needs here to be + * patched to use the nullptr. + * XXX to be changed if it is clear how to support better XXX + */ + ss.ss_sp = 0; /* <-- patch contrib code here XXX */ + ss.ss_size = 64 * 1024; /* STACK_SIZE; */ + ss.ss_flags = 0; + if (sigaltstack(&ss, &ss_old) < 0) { + abort(); + } + + /* trigger SIGUSR2 */ + thread_state.called = 0; + kill(getpid(), SIGUSR2); + + /* wait for signal executed */ + sigfillset(&sigs); + sigdelset (&sigs, SIGUSR2); + while (!thread_state.called) { + sigsuspend(&sigs); + } + + /* disable alternative signal test */ + sigaltstack(NULL, &ss); + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) < 0) { + abort(); + } + + sigaltstack(NULL, &ss); + if (!(ss_old.ss_flags & SS_DISABLE)) { + sigaltstack(&ss_old, NULL); + } + + /* restore old sigusr2 signal handler */ + sigaction(SIGUSR2, &sa_old, NULL); + + printf("%s done\n", __func__); +}