[tip: x86/urgent] x86/entry/vdso32: Work around libgcc unwinder bug

tip-bot2 for H. Peter Anvin posted 1 patch 1 month ago
arch/x86/entry/vdso/vdso32/sigreturn.S | 30 +++++++++++++++++++++++++-
1 file changed, 30 insertions(+)
[tip: x86/urgent] x86/entry/vdso32: Work around libgcc unwinder bug
Posted by tip-bot2 for H. Peter Anvin 1 month ago
The following commit has been merged into the x86/urgent branch of tip:

Commit-ID:     b5ef09a77d0b5213268300eedd8a7d28b4e92d47
Gitweb:        https://git.kernel.org/tip/b5ef09a77d0b5213268300eedd8a7d28b4e92d47
Author:        H. Peter Anvin <hpa@zytor.com>
AuthorDate:    Thu, 26 Feb 2026 17:03:07 -08:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 04 Mar 2026 17:06:08 +01:00

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
---
 arch/x86/entry/vdso/vdso32/sigreturn.S | 30 +++++++++++++++++++++++++-
 1 file changed, 30 insertions(+)

diff --git a/arch/x86/entry/vdso/vdso32/sigreturn.S b/arch/x86/entry/vdso/vdso32/sigreturn.S
index b433353..b33fcc5 100644
--- a/arch/x86/entry/vdso/vdso32/sigreturn.S
+++ b/arch/x86/entry/vdso/vdso32/sigreturn.S
@@ -35,9 +35,38 @@
 #endif
 .endm
 
+/*
+ * WARNING:
+ *
+ * A bug in the libgcc unwinder as of at least gcc 15.2 (2026) means that
+ * the unwinder fails to recognize the signal frame flag.
+ *
+ * There is a hacky legacy fallback path in libgcc which ends up
+ * getting invoked instead. It happens to work as long as BOTH of the
+ * following conditions are true:
+ *
+ * 1. There is at least one byte before the each of the sigreturn
+ *    functions which falls outside any function. This is enforced by
+ *    an explicit nop instruction before the ALIGN.
+ * 2. The code sequences between the entry point up to and including
+ *    the int $0x80 below need to match EXACTLY. Do not change them
+ *    in any way. The exact byte sequences are:
+ *
+ *    __kernel_sigreturn:
+ *        0:   58                      pop    %eax
+ *        1:   b8 77 00 00 00          mov    $0x77,%eax
+ *        6:   cd 80                   int    $0x80
+ *
+ *    __kernel_rt_sigreturn:
+ *        0:   b8 ad 00 00 00          mov    $0xad,%eax
+ *        5:   cd 80                   int    $0x80
+ *
+ * For details, see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124050
+ */
 	.text
 	.globl __kernel_sigreturn
 	.type __kernel_sigreturn,@function
+	nop			/* libgcc hack: see comment above */
 	ALIGN
 __kernel_sigreturn:
 	STARTPROC_SIGNAL_FRAME IA32_SIGFRAME_sigcontext
@@ -52,6 +81,7 @@ SYM_INNER_LABEL(vdso32_sigreturn_landing_pad, SYM_L_GLOBAL)
 
 	.globl __kernel_rt_sigreturn
 	.type __kernel_rt_sigreturn,@function
+	nop			/* libgcc hack: see comment above */
 	ALIGN
 __kernel_rt_sigreturn:
 	STARTPROC_SIGNAL_FRAME IA32_RT_SIGFRAME_sigcontext