[PW_SID:1101637] riscv: Add reliable stack unwinding for livepatch#2024
[PW_SID:1101637] riscv: Add reliable stack unwinding for livepatch#2024linux-riscv-bot wants to merge 8 commits into
Conversation
RISC-V uses -fpatchable-function-entry=8,4 when the compressed ISA is enabled and -fpatchable-function-entry=4,2 otherwise. In both cases, the patchable NOP area starts 8 bytes before the function symbol address. The __mcount_loc entries therefore point at the patchable NOP area associated with a function, while nm reports the function symbol at the entry address used for the function range check. After RISC-V selected HAVE_BUILDTIME_MCOUNT_SORT, sorttable started applying that range check at build time. Without allowing entries just before the reported function address, the mcount sorter treats valid RISC-V ftrace callsites as invalid weak-function entries and writes them back as zero. The resulting kernel boots with no ftrace entries, breaking dynamic ftrace and users such as livepatch. The failure is silent during the final link because zeroing weak-function entries is an expected sorttable operation. At boot, those zero entries are skipped by ftrace_process_locs(), so the only obvious symptom is that the vmlinux ftrace table has lost valid callsites and ftrace users cannot attach to them. CONFIG_FTRACE_SORT_STARTUP_TEST also reports the table as sorted in this state: it only checks that the __mcount_loc entries are in ascending order, which a fully zeroed table trivially satisfies. The original commit relied on this check and did not see the regression. On an affected RISC-V QEMU boot with both CONFIG_FTRACE_SORT_STARTUP_TEST and CONFIG_FTRACE_STARTUP_TEST enabled, the sort check still passes while ftrace reports zero usable entries and the early selftests fail: [ 0.000000] ftrace section at ffffffff8101da98 sorted properly [ 0.000000] ftrace: allocating 0 entries in 128 pages [ 0.054999] Testing tracer function: .. no entries found ..FAILED! [ 0.172407] tracer: function failed selftest, disabling [ 0.178186] Failed to init function_graph tracer, init returned -19 Handle RISC-V like arm64 for the function-range check and allow patchable entries up to 8 bytes before the function address. With this fix, a RISC-V QEMU smoke boot with ftrace startup tests shows the vmlinux ftrace table is populated and dynamic ftrace still works: [ 0.000000] ftrace: allocating 46749 entries in 184 pages [ 0.051115] Testing tracer function: PASSED [ 1.283782] Testing dynamic ftrace: PASSED [ 6.275456] Testing tracer function_graph: PASSED Fixes: 0ca1724 ("riscv: ftrace: select HAVE_BUILDTIME_MCOUNT_SORT") Signed-off-by: Wang Han <wanghan@linux.alibaba.com> Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
Reliable frame-pointer unwinding needs an explicit way to identify
exception boundaries and the final entry frame. The existing unwinder
infers those boundaries from return addresses, which is too loose for a
future reliable unwinder.
Add a small metadata frame record to pt_regs and initialize it on
exception entry, kernel thread fork, user fork, and early idle task
setup. The record uses a zero {fp, ra} sentinel plus a type field so a
later unwinder can distinguish a final user-to-kernel boundary from a
nested kernel pt_regs boundary.
This follows the arm64 metadata frame-record model, adapted to the
RISC-V {fp, ra} frame record convention.
The metadata is established at the RISC-V entry boundaries that need an
explicit unwind marker:
* exception entry clears the metadata {fp, ra} pair and uses SPP
(or MPP in M-mode) to record whether the pt_regs frame is the final
user-to-kernel boundary or a nested kernel boundary;
* _start_kernel builds the init task's final metadata record, while
the secondary CPU path sets up s0 before smp_callin() so idle-task
unwinding does not inherit an undefined caller frame;
* copy_thread creates matching final metadata records for new kernel
and user tasks, and keeps s0 available for the frame-pointer chain;
* call_on_irq_stack still reserves an aligned stack slot, but links the
saved {fp, ra} with the raw frame-record size so s0 points at the
RISC-V frame record rather than past the alignment padding.
These changes keep s0 reserved for the frame-pointer chain at task and
stack-switch boundaries.
Signed-off-by: Wang Han <wanghan@linux.alibaba.com>
Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
KASAN records stack traces for every alloc/free, which means it walks the unwinder very frequently. Instrumenting the stack trace collection code itself adds substantial overhead and makes the traces themselves noisier. Mark stacktrace.o as not KASAN-instrumented, matching the arm, arm64 and x86 treatment of their stack unwinding code. This is a prerequisite preference for the upcoming reliable unwinder, but the change is valid on its own. Signed-off-by: Wang Han <wanghan@linux.alibaba.com> Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
The dynamic ftrace entry/exit only saved s0 (the architectural frame pointer) when HAVE_FUNCTION_GRAPH_FP_TEST was selected. The upcoming reliable frame-pointer unwinder needs s0 to be present in ftrace_regs unconditionally so it can use the frame pointer as the function-graph return-address cookie regardless of FP_TEST. Save and restore s0 unconditionally in the dynamic ftrace ABI register frame. The cost is one extra REG_S/REG_L pair per traced call, which is negligible compared to the overall ftrace cost; the benefit is a consistent ftrace_regs layout for the unwinder. Signed-off-by: Wang Han <wanghan@linux.alibaba.com> Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
A reliable unwinder needs to validate that every frame record it reads
is fully contained in a known kernel stack, and it needs to refuse to
walk back into a stack it has already left. Add the building blocks
for that:
* struct stack_info / struct unwind_state in a new
asm/stacktrace/common.h, modelled on the arm64 reference
implementation.
* stackinfo_get_irq() / stackinfo_get_task() / stackinfo_get_overflow()
plus the corresponding on_*_stack() predicates in asm/stacktrace.h,
so callers can ask "is this object on stack X?" by stack kind
rather than open-coded address arithmetic.
* unwind_init_common(), unwind_find_stack() and
unwind_consume_stack() helpers that enforce the
forward-progress-only invariant required for reliability.
No existing user is wired up to these helpers in this commit; the
unwinder switch comes in a follow-up. The header changes leave
on_thread_stack() with the same semantics as before, just expressed in
terms of the new helpers.
Signed-off-by: Wang Han <wanghan@linux.alibaba.com>
Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
Replace the open-coded frame-pointer walker in arch_stack_walk() with a
robust kunwind state machine, modelled on arch/arm64/kernel/stacktrace.c
and retargeted to the RISC-V {fp, ra} frame record convention. The new
walker tracks stack bounds, consumes frame records monotonically,
understands the metadata pt_regs records added in the previous frame
record metadata patch, and recovers return addresses replaced by
function graph tracing and kretprobes.
This commit introduces arch_stack_walk_reliable() but does not yet
select HAVE_RELIABLE_STACKTRACE; that is done in a follow-up Kconfig
patch so this commit can be reviewed and bisected as a pure unwinder
replacement. Until that Kconfig change lands, livepatch is not yet
enabled and arch_stack_walk_reliable() has no in-tree caller.
Three related callers are updated to keep the same frame-record
assumptions everywhere:
* Function graph tracing: the old RISC-V unwinder matched function
graph return-stack entries by the saved return-address slot. That
was consistent with the static mcount path, but not with the dynamic
ftrace path where the parent slot is ftrace_regs::ra. Use the
architectural frame pointer as the function graph return-address
cookie, matching the kunwind walker.
* Perf callchains: route kernel callchain collection through
arch_stack_walk() so perf sees the same frame-pointer unwind
behaviour as dump_stack() and the upcoming livepatch path.
* dump_backtrace() / __get_wchan() / show_stack(): these now go
through arch_stack_walk(); the explicit "Call Trace:" header is
moved into dump_backtrace() to preserve the original output.
The non-frame-pointer fallback walker is kept untouched for
!CONFIG_FRAME_POINTER builds.
Signed-off-by: Wang Han <wanghan@linux.alibaba.com>
Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
Now that the metadata frame records, the kunwind state machine and
arch_stack_walk_reliable() are all in place, advertise the capability
to the rest of the kernel:
* select HAVE_RELIABLE_STACKTRACE under FRAME_POINTER && 64BIT, so
only the configurations that actually have the metadata records
and the FP-based reliable walker enable it.
* select HAVE_LIVEPATCH under the same condition and source
kernel/livepatch/Kconfig so the livepatch menu is reachable from
the RISC-V configuration.
This is split out from the unwinder change so the policy decision and
the implementation can be reviewed and reverted independently.
Signed-off-by: Wang Han <wanghan@linux.alibaba.com>
Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
The syscall livepatch selftest resolves and patches a syscall wrapper symbol. To use that test for RISC-V livepatch validation, add the RISC-V FN_PREFIX definition for ARCH_HAS_SYSCALL_WRAPPER. Without this macro, the syscall livepatch selftest cannot resolve the RISC-V target symbol, and the syscall-related livepatch test fails on RISC-V. Signed-off-by: Wang Han <wanghan@linux.alibaba.com> Signed-off-by: Linux RISC-V bot <linux.riscv.bot@gmail.com>
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 1: "[1/8] scripts/sorttable: Handle RISC-V patchable ftrace entries" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 2: "[2/8] riscv: stacktrace: Add frame record metadata" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 6: "[6/8] riscv: stacktrace: switch to frame-pointer based unwinder" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 7: "[7/8] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
|
Patch 8: "[8/8] selftests/livepatch: Add RISC-V syscall wrapper prefix" |
PR for series 1101637 applied to workflow__riscv__for-next
Name: riscv: Add reliable stack unwinding for livepatch
URL: https://patchwork.kernel.org/project/linux-riscv/list/?series=1101637
Version: 1