Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 884961618ee5 ("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

authored by

H. Peter Anvin and committed by
Peter Zijlstra
b5ef09a7 59674fc9

+30
+30
arch/x86/entry/vdso/vdso32/sigreturn.S
··· 35 35 #endif 36 36 .endm 37 37 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 + */ 38 66 .text 39 67 .globl __kernel_sigreturn 40 68 .type __kernel_sigreturn,@function 69 + nop /* libgcc hack: see comment above */ 41 70 ALIGN 42 71 __kernel_sigreturn: 43 72 STARTPROC_SIGNAL_FRAME IA32_SIGFRAME_sigcontext ··· 81 52 82 53 .globl __kernel_rt_sigreturn 83 54 .type __kernel_rt_sigreturn,@function 55 + nop /* libgcc hack: see comment above */ 84 56 ALIGN 85 57 __kernel_rt_sigreturn: 86 58 STARTPROC_SIGNAL_FRAME IA32_RT_SIGFRAME_sigcontext