sigaltstack from signal handler succeeds but is overwritten on sigreturn

From: Jim Newsome
Date: Wed Mar 02 2022 - 10:08:24 EST


Context: I recently came across this behavior while developing [shadow], where we're using seccomp to trap syscalls to an LD_PRELOAD'd signal handler, as a fallback for syscalls we weren't able to intercept more efficiently at the libc API level via LD_PRELOAD. When a new thread is created, we do some self-initialization on its first intercepted syscall, including setting up a signal stack with sigaltstack. When the first syscall is trapped via seccomp, this initialization happens in the sigsys signal handler, and the sigaltstack configuration is lost on return. We can work around this behavior by initializing explicitly immediately after returning from clone in the child thread (which is probably a better design anyway), but it took a while to figure out what was going wrong.

[shadow]: https://github.com/shadow/shadow

Here is a simplified demonstration of the issue: https://godbolt.org/z/Mrxe119oj

From the [sigaltstack man page], I'd only expect sigreturn to restore the sigaltstack configuration if there was already a sigaltstack configured for the thread on entry to the handler, and it had SS_AUTODISARM set.

[sigaltstack man page]: https://man7.org/linux/man-pages/man2/sigaltstack.2.html

I discovered this on x86-64 Ubuntu and their modified kernel 5.13.0-30-generic and am not currently set up to try reproducing on a vanilla kernel, but if I'm reading the source correctly, this behavior exists in the latest kernel, at least on x86-64. The x86-64 [sigreturn] always calls [restore_altstack], which always restores the old sigaltstack config.

[sigreturn]: https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/arch/x86/kernel/signal.c#L656

[restore_altstack]: https://github.com/torvalds/linux/blob/a4fd49cdb5495f36a35bd27b69b3806e383c719b/kernel/signal.c#L4246

Is this unconditional restore intended? If so, maybe it could be documented more explicitly in the sigaltstack and/or sigreturn man pages?