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?