diff options
| author | Peter Zijlstra <peterz@infradead.org> | 2025-10-24 12:31:10 +0200 |
|---|---|---|
| committer | Peter Zijlstra <peterz@infradead.org> | 2025-10-29 10:29:58 +0100 |
| commit | ae25884ad749e7f6e0c3565513bdc8aa2554a425 (patch) | |
| tree | 373450384ff724ce72856374d5464dd3dc7abe22 /kernel/unwind | |
| parent | 49cf34c0815f93fb2ea3ab5cfbac1124bd9b45d0 (diff) | |
| download | linux-next-ae25884ad749e7f6e0c3565513bdc8aa2554a425.tar.gz linux-next-ae25884ad749e7f6e0c3565513bdc8aa2554a425.zip | |
unwind_user/x86: Teach FP unwind about start of function
When userspace is interrupted at the start of a function, before we
get a chance to complete the frame, unwind will miss one caller.
X86 has a uprobe specific fixup for this, add bits to the generic
unwinder to support this.
Suggested-by: Jens Remus <jremus@linux.ibm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20251024145156.GM4068168@noisy.programming.kicks-ass.net
Diffstat (limited to 'kernel/unwind')
| -rw-r--r-- | kernel/unwind/user.c | 39 |
1 files changed, 30 insertions, 9 deletions
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c index 642871527a13..39e270789444 100644 --- a/kernel/unwind/user.c +++ b/kernel/unwind/user.c @@ -26,14 +26,12 @@ get_user_word(unsigned long *word, unsigned long base, int off, unsigned int ws) return get_user(*word, addr); } -static int unwind_user_next_fp(struct unwind_user_state *state) +static int unwind_user_next_common(struct unwind_user_state *state, + const struct unwind_user_frame *frame) { - const struct unwind_user_frame frame = { - ARCH_INIT_USER_FP_FRAME(state->ws) - }; unsigned long cfa, fp, ra; - if (frame.use_fp) { + if (frame->use_fp) { if (state->fp < state->sp) return -EINVAL; cfa = state->fp; @@ -42,7 +40,7 @@ static int unwind_user_next_fp(struct unwind_user_state *state) } /* Get the Canonical Frame Address (CFA) */ - cfa += frame.cfa_off; + cfa += frame->cfa_off; /* stack going in wrong direction? */ if (cfa <= state->sp) @@ -53,19 +51,41 @@ static int unwind_user_next_fp(struct unwind_user_state *state) return -EINVAL; /* Find the Return Address (RA) */ - if (get_user_word(&ra, cfa, frame.ra_off, state->ws)) + if (get_user_word(&ra, cfa, frame->ra_off, state->ws)) return -EINVAL; - if (frame.fp_off && get_user_word(&fp, cfa, frame.fp_off, state->ws)) + if (frame->fp_off && get_user_word(&fp, cfa, frame->fp_off, state->ws)) return -EINVAL; state->ip = ra; state->sp = cfa; - if (frame.fp_off) + if (frame->fp_off) state->fp = fp; + state->topmost = false; return 0; } +static int unwind_user_next_fp(struct unwind_user_state *state) +{ +#ifdef CONFIG_HAVE_UNWIND_USER_FP + struct pt_regs *regs = task_pt_regs(current); + + if (state->topmost && unwind_user_at_function_start(regs)) { + const struct unwind_user_frame fp_entry_frame = { + ARCH_INIT_USER_FP_ENTRY_FRAME(state->ws) + }; + return unwind_user_next_common(state, &fp_entry_frame); + } + + const struct unwind_user_frame fp_frame = { + ARCH_INIT_USER_FP_FRAME(state->ws) + }; + return unwind_user_next_common(state, &fp_frame); +#else + return -EINVAL; +#endif +} + static int unwind_user_next(struct unwind_user_state *state) { unsigned long iter_mask = state->available_types; @@ -118,6 +138,7 @@ static int unwind_user_start(struct unwind_user_state *state) state->done = true; return -EINVAL; } + state->topmost = true; return 0; } |
