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
This commit is contained in:
Alexander Boettcher 2024-07-18 15:56:15 +02:00 committed by Christian Helmuth
parent 0c5df0036c
commit a798f70284
4 changed files with 151 additions and 11 deletions

View File

@ -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))

View File

@ -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()
{
/*

View File

@ -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;
}

View File

@ -32,8 +32,14 @@ extern "C" {
#include <time.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <pthread.h>
}
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__);
}