-
Notifications
You must be signed in to change notification settings - Fork 269
Implement ucontext_t parameter in signal handlers #1975
Description
We currently pass NULL as the 3rd parameter to sa_sigaction signal handlers. This ought to instead be a valid ucontext_t*.
In Linux, this would normally contain state about the running thread just before the signal was delivered. Its contents are described in getcontext(3).
Parts of ucontext_t are opaque, making it difficult to construct completely synthetically. We can obtain one though by calling getcontext, swapcontext, or handling a native signal ourselves.
I think the main important use-case to support is being able to use it to call swapcontext or setcontext from the managed
program's signal handler. golang does this (or some equivalent) to implement its preemptive goroutine scheduling. For this purpose, some possibilities are:
- Call the managed process signal handler via
swapcontext, and pass the pointer to the swapped-out context. We already useswapcontextwhen the signal handler is configured to run on an alternate stack. I've locally confirmed this produces the desired behavior in snowflake simulations. Doing this is a little tricky though in the case where the handler doesn't run on a different stack, sinceswapcontextalways switches stacks. Maybe we could set up an appropriate stack starting from the current position in the current stack, but this seems fragile. - Set up a signal handler for some signal, like
SIGUSR1, andraisethat signal to kick off our signal handling. We could then pass theucontext_tfrom our own signal handler on to the managed process signal handlers. I think this would correctly handle both the cases where the handler is or isn't configured to run on an alternate stack, but I haven't tried it yet.
There's another use-case where the handler might deeply inspect the ucontext_t itself, e.g. to see register values at the point where the signal was raised. We do this ourselves in our SIGSYS handler to extract the syscall parameters of the syscall that raised the signal. It's also possible to mutate individual register values there, which will cause those to be restored when the handler returns.
AFAIK this use case only really makes sense for "synchronous" signals like SIGSYS, SIGSEGV, and SIGILL. We don't really support these at all yet. It'd be straightforward though to support them by installing our own handlers for those signals, which then delegate to the managed process configured handler (if any). In that case I think it'd make sense to pass through the ucontext_t that our handler received.