Skip to content

Commit b5ef09a

Browse files
H. Peter AnvinPeter Zijlstra
authored andcommitted
x86/entry/vdso32: Work around libgcc unwinder bug
The unwinder code in libgcc has a long standing bug which causes it to fail to pick up the signal frame CFI flag. This is a generic bug across all platforms. It affects the __kernel_sigreturn and __kernel_rt_sigreturn vdso entry points on i386. The x86-64 kernel doesn't provide a sigreturn stub, and so there is no kernel-provided code that is affected on x86-64. libgcc does have a legacy fallback path which happens to work as long as the bytes immediately before each of the sigreturn functions fall outside any function. This patch adds a nop before the ALIGN to each of the sigreturn stubs to ensure that this is, indeed, the case. The rest of the patch is just a comment which documents the invariants that need to be maintained for this legacy path to work correctly. This is a manifest bug: in the current vdso, __kernel_vsyscall is a multiple of 16 bytes long and thus __kernel_sigreturn does not have any padding in front of it. Closes: https://lore.kernel.org/lkml/f3412cc3e8f66d1853cc9d572c0f2fab076872b1.camel@xry111.site Fixes: 8849616 ("x86/entry/vdso32: Remove open-coded DWARF in sigreturn.S") Reported-by: Xi Ruoyao <xry111@xry111.site> Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124050 Link: https://patch.msgid.link/20260227010308.310342-1-hpa@zytor.com
1 parent 59674fc commit b5ef09a

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

arch/x86/entry/vdso/vdso32/sigreturn.S

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,38 @@
3535
#endif
3636
.endm
3737

38+
/*
39+
* WARNING:
40+
*
41+
* A bug in the libgcc unwinder as of at least gcc 15.2 (2026) means that
42+
* the unwinder fails to recognize the signal frame flag.
43+
*
44+
* There is a hacky legacy fallback path in libgcc which ends up
45+
* getting invoked instead. It happens to work as long as BOTH of the
46+
* following conditions are true:
47+
*
48+
* 1. There is at least one byte before the each of the sigreturn
49+
* functions which falls outside any function. This is enforced by
50+
* an explicit nop instruction before the ALIGN.
51+
* 2. The code sequences between the entry point up to and including
52+
* the int $0x80 below need to match EXACTLY. Do not change them
53+
* in any way. The exact byte sequences are:
54+
*
55+
* __kernel_sigreturn:
56+
* 0: 58 pop %eax
57+
* 1: b8 77 00 00 00 mov $0x77,%eax
58+
* 6: cd 80 int $0x80
59+
*
60+
* __kernel_rt_sigreturn:
61+
* 0: b8 ad 00 00 00 mov $0xad,%eax
62+
* 5: cd 80 int $0x80
63+
*
64+
* For details, see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124050
65+
*/
3866
.text
3967
.globl __kernel_sigreturn
4068
.type __kernel_sigreturn,@function
69+
nop /* libgcc hack: see comment above */
4170
ALIGN
4271
__kernel_sigreturn:
4372
STARTPROC_SIGNAL_FRAME IA32_SIGFRAME_sigcontext
@@ -52,6 +81,7 @@ SYM_INNER_LABEL(vdso32_sigreturn_landing_pad, SYM_L_GLOBAL)
5281

5382
.globl __kernel_rt_sigreturn
5483
.type __kernel_rt_sigreturn,@function
84+
nop /* libgcc hack: see comment above */
5585
ALIGN
5686
__kernel_rt_sigreturn:
5787
STARTPROC_SIGNAL_FRAME IA32_RT_SIGFRAME_sigcontext

0 commit comments

Comments
 (0)