Skip to content

Commit 87676cf

Browse files
committed
arm64: vdso: Disable dwarf unwinding through the sigreturn trampoline
Commit 7e9f5e6 ("arm64: vdso: Add --eh-frame-hdr to ldflags") results in a .eh_frame_hdr section for the vDSO, which in turn causes the libgcc unwinder to unwind out of signal handlers using the .eh_frame information populated by our .cfi directives. In conjunction with a4eb355 ("arm64: vdso: Fix CFI directives in sigreturn trampoline"), this has been shown to cause segmentation faults originating from within the unwinder during thread cancellation: | Thread 14 "virtio-net-rx" received signal SIGSEGV, Segmentation fault. | 0x0000000000435e24 in uw_frame_state_for () | (gdb) bt | #0 0x0000000000435e24 in uw_frame_state_for () | #1 0x0000000000436e88 in _Unwind_ForcedUnwind_Phase2 () | #2 0x00000000004374d8 in _Unwind_ForcedUnwind () | #3 0x0000000000428400 in __pthread_unwind (buf=<optimized out>) at unwind.c:121 | #4 0x0000000000429808 in __do_cancel () at ./pthreadP.h:304 | #5 sigcancel_handler (sig=32, si=0xffff33c743f0, ctx=<optimized out>) at nptl-init.c:200 | #6 sigcancel_handler (sig=<optimized out>, si=0xffff33c743f0, ctx=<optimized out>) at nptl-init.c:165 | #7 <signal handler called> | #8 futex_wait_cancelable (private=0, expected=0, futex_word=0x3890b708) at ../sysdeps/unix/sysv/linux/futex-internal.h:88 After considerable bashing of heads, it appears that our CFI directives for unwinding out of the sigreturn trampoline are only processed by libgcc when both a .eh_frame_hdr section is present *and* the mysterious NOP is covered by an entry in .eh_frame. With both of these now in place, it has highlighted that our CFI directives are not comprehensive enough to restore the stack pointer of the interrupted context. This results in libgcc falling back to an arm64-specific unwinder after computing a bogus PC value from the unwind tables. The unwinder promptly dereferences this bogus address in an attempt to see if the pointed-to instruction sequence looks like the sigreturn trampoline. Restore the old unwind behaviour, which relied solely on heuristics in the unwinder, by removing the .eh_frame_hdr section from the vDSO and commenting out the insufficient CFI directives for now. Add comments to explain the current, miserable state of affairs. Cc: Tamas Zsoldos <[email protected]> Cc: Szabolcs Nagy <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Daniel Kiss <[email protected]> Acked-by: Dave Martin <[email protected]> Reviewed-by: Vincenzo Frascino <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> Reported-by: Ard Biesheuvel <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent 24ebec2 commit 87676cf

File tree

2 files changed

+35
-21
lines changed

2 files changed

+35
-21
lines changed

arch/arm64/kernel/vdso/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti
2424
# routines, as x86 does (see 6f121e548f83 ("x86, vdso: Reimplement vdso.so
2525
# preparation in build-time C")).
2626
ldflags-y := -shared -nostdlib -soname=linux-vdso.so.1 --hash-style=sysv \
27-
-Bsymbolic --eh-frame-hdr --build-id -n $(btildflags-y) -T
27+
-Bsymbolic --no-eh-frame-hdr --build-id -n $(btildflags-y) -T
2828

2929
ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18
3030
ccflags-y += -DDISABLE_BRANCH_PROFILING

arch/arm64/kernel/vdso/sigreturn.S

+34-20
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,40 @@
1818

1919
.text
2020

21+
/*
22+
* NOTE!!! You may notice that all of the .cfi directives in this file have
23+
* been commented out. This is because they have been shown to trigger segfaults
24+
* in libgcc when unwinding out of a SIGCANCEL handler to invoke pthread
25+
* cleanup handlers during the thread cancellation dance. By omitting the
26+
* directives, we trigger an arm64-specific fallback path in the unwinder which
27+
* recognises the signal frame and restores many of the registers directly from
28+
* the sigcontext. Re-enabling the cfi directives here therefore needs to be
29+
* much more comprehensive to reduce the risk of further regressions.
30+
*/
31+
2132
/* Ensure that the mysterious NOP can be associated with a function. */
22-
.cfi_startproc
33+
// .cfi_startproc
2334

2435
/*
25-
* .cfi_signal_frame causes the corresponding Frame Description Entry in the
26-
* .eh_frame section to be annotated as a signal frame. This allows DWARF
27-
* unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo(), which permits
28-
* unwinding out of the signal trampoline without the need for the mysterious
29-
* NOP.
36+
* .cfi_signal_frame causes the corresponding Frame Description Entry (FDE) in
37+
* the .eh_frame section to be annotated as a signal frame. This allows DWARF
38+
* unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo() and identify
39+
* the next frame using the unmodified return address instead of subtracting 1,
40+
* which may yield the wrong FDE.
3041
*/
31-
.cfi_signal_frame
42+
// .cfi_signal_frame
3243

3344
/*
3445
* Tell the unwinder where to locate the frame record linking back to the
35-
* interrupted context. We don't provide unwind info for registers other
36-
* than the frame pointer and the link register here; in practice, this
37-
* is sufficient for unwinding in C/C++ based runtimes and the values in
38-
* the sigcontext may have been modified by this point anyway. Debuggers
46+
* interrupted context. We don't provide unwind info for registers other than
47+
* the frame pointer and the link register here; in practice, this is likely to
48+
* be insufficient for unwinding in C/C++ based runtimes, especially without a
49+
* means to restore the stack pointer. Thankfully, unwinders and debuggers
3950
* already have baked-in strategies for attempting to unwind out of signals.
4051
*/
41-
.cfi_def_cfa x29, 0
42-
.cfi_offset x29, 0 * 8
43-
.cfi_offset x30, 1 * 8
52+
// .cfi_def_cfa x29, 0
53+
// .cfi_offset x29, 0 * 8
54+
// .cfi_offset x30, 1 * 8
4455

4556
/*
4657
* This mysterious NOP is required for some unwinders (e.g. libc++) that
@@ -51,16 +62,19 @@
5162
nop // Mysterious NOP
5263

5364
/*
54-
* GDB relies on being able to identify the sigreturn instruction sequence to
55-
* unwind from signal handlers. We cannot, therefore, use SYM_FUNC_START()
56-
* here, as it will emit a BTI C instruction and break the unwinder. Thankfully,
57-
* this function is only ever called from a RET and so omitting the landing pad
58-
* is perfectly fine.
65+
* GDB, libgcc and libunwind rely on being able to identify the sigreturn
66+
* instruction sequence to unwind from signal handlers. We cannot, therefore,
67+
* use SYM_FUNC_START() here, as it will emit a BTI C instruction and break the
68+
* unwinder. Thankfully, this function is only ever called from a RET and so
69+
* omitting the landing pad is perfectly fine.
5970
*/
6071
SYM_CODE_START(__kernel_rt_sigreturn)
72+
// PLEASE DO NOT MODIFY
6173
mov x8, #__NR_rt_sigreturn
74+
// PLEASE DO NOT MODIFY
6275
svc #0
63-
.cfi_endproc
76+
// PLEASE DO NOT MODIFY
77+
// .cfi_endproc
6478
SYM_CODE_END(__kernel_rt_sigreturn)
6579

6680
emit_aarch64_feature_1_and

0 commit comments

Comments
 (0)