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.
Unified · Split
| @@ -35,9 +35,38 @@ · 34 unmodified lines | |||
| 35 | #endif | 35 | #endif |
| 36 | .endm | 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 | .text | 66 | .text |
| 39 | .globl __kernel_sigreturn | 67 | .globl __kernel_sigreturn |
| 40 | .type __kernel_sigreturn,@function | 68 | .type __kernel_sigreturn,@function |
| 69 | nop /* libgcc hack: see comment above */ | ||
| 41 | ALIGN | 70 | ALIGN |
| 42 | __kernel_sigreturn: | 71 | __kernel_sigreturn: |
| 43 | STARTPROC_SIGNAL_FRAME IA32_SIGFRAME_sigcontext | 72 | STARTPROC_SIGNAL_FRAME IA32_SIGFRAME_sigcontext |
| @@ -52,6 +81,7 @@ SYM_INNER_LABEL(vdso32_sigreturn_landing_pad, SYM_L_GLOBAL) · 8 unmodified lines | |||
| 52 | 81 | ||
| 53 | .globl __kernel_rt_sigreturn | 82 | .globl __kernel_rt_sigreturn |
| 54 | .type __kernel_rt_sigreturn,@function | 83 | .type __kernel_rt_sigreturn,@function |
| 84 | nop /* libgcc hack: see comment above */ | ||
| 55 | ALIGN | 85 | ALIGN |
| 56 | __kernel_rt_sigreturn: | 86 | __kernel_rt_sigreturn: |
| 57 | STARTPROC_SIGNAL_FRAME IA32_RT_SIGFRAME_sigcontext | 87 | STARTPROC_SIGNAL_FRAME IA32_RT_SIGFRAME_sigcontext |