* [PATCH 0/2] x86 unwind fixes (1 unwinder fix, 1 missing entry ORC annotation)
@ 2025-03-25 2:01 Jann Horn
2025-03-25 2:01 ` [PATCH 1/2] x86/entry: Fix ORC for PUSH_REGS with save_ret=1 Jann Horn
2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn
0 siblings, 2 replies; 8+ messages in thread
From: Jann Horn @ 2025-03-25 2:01 UTC (permalink / raw)
To: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra
Cc: linux-kernel, Dominik Brodowski, Ingo Molnar, Oleg Nesterov,
Vernon Lovejoy, Jann Horn
While doing some kernel development, I managed to cause a recursive #PF
ending in a #DF, as one does... Thanks Andy for adding guard stacks and
such nice error handling for them years ago.
Unfortunately, instead of a nice stack trace, I just got a pile of guess
unwind lines, caused by two bugs.
Here are fixes I came up with for these issues. The issues were
introduced in 2018 and 2023 - if nobody else noticed them since then,
I guess the fixes aren't particularly urgent.
To: Andy Lutomirski <luto@kernel.org>
Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Cc: Ingo Molnar <mingo@kernel.org>
To: Josh Poimboeuf <jpoimboe@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Vernon Lovejoy <vlovejoy@redhat.com>
Signed-off-by: Jann Horn <jannh@google.com>
---
Jann Horn (2):
x86/entry: Fix ORC for PUSH_REGS with save_ret=1
x86/dumpstack: Fix broken unwinding from exception stacks
arch/x86/entry/calling.h | 2 ++
arch/x86/kernel/dumpstack.c | 5 ++---
2 files changed, 4 insertions(+), 3 deletions(-)
---
base-commit: bcb044256d3f5d9f5bb61d1eac6492f77883bd60
change-id: 20250325-2025-03-unwind-fixes-a367c54be822
--
Jann Horn <jannh@google.com>
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/2] x86/entry: Fix ORC for PUSH_REGS with save_ret=1 2025-03-25 2:01 [PATCH 0/2] x86 unwind fixes (1 unwinder fix, 1 missing entry ORC annotation) Jann Horn @ 2025-03-25 2:01 ` Jann Horn 2025-03-25 7:46 ` [tip: x86/urgent] x86/entry: Fix ORC unwinder " tip-bot2 for Jann Horn 2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn 1 sibling, 1 reply; 8+ messages in thread From: Jann Horn @ 2025-03-25 2:01 UTC (permalink / raw) To: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra Cc: linux-kernel, Dominik Brodowski, Ingo Molnar, Oleg Nesterov, Vernon Lovejoy, Jann Horn PUSH_REGS with save_ret=1 is used by interrupt entry helper functions that initially start with a UNWIND_HINT_FUNC ORC state. However, save_ret=1 means that we clobber the helper function's return address (and then later restore the return address further down on the stack); after that point, the only thing on the stack we can unwind through is the IRET frame, so use UNWIND_HINT_IRET_REGS until we have a full pt_regs frame. (An alternate approach would be to move the pt_regs->di overwrite down such that it is the final step of pt_regs setup; but I don't want to rearrange entry code just to make unwinding a tiny bit more elegant.) Fixes: 9e809d15d6b6 ("x86/entry: Reduce the code footprint of the 'idtentry' macro") Signed-off-by: Jann Horn <jannh@google.com> --- arch/x86/entry/calling.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index ea81770629ee..b14a2c39a601 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h @@ -70,6 +70,8 @@ For 32-bit we have the following conventions - kernel is built with pushq %rsi /* pt_regs->si */ movq 8(%rsp), %rsi /* temporarily store the return address in %rsi */ movq %rdi, 8(%rsp) /* pt_regs->di (overwriting original return address) */ + /* we just clobbered the return address - use the iret frame for unwinding */ + UNWIND_HINT_IRET_REGS offset=3*8 .else pushq %rdi /* pt_regs->di */ pushq %rsi /* pt_regs->si */ -- 2.49.0.395.g12beb8f557-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [tip: x86/urgent] x86/entry: Fix ORC unwinder for PUSH_REGS with save_ret=1 2025-03-25 2:01 ` [PATCH 1/2] x86/entry: Fix ORC for PUSH_REGS with save_ret=1 Jann Horn @ 2025-03-25 7:46 ` tip-bot2 for Jann Horn 0 siblings, 0 replies; 8+ messages in thread From: tip-bot2 for Jann Horn @ 2025-03-25 7:46 UTC (permalink / raw) To: linux-tip-commits Cc: Jann Horn, Ingo Molnar, Andy Lutomirski, Brian Gerst, Juergen Gross, H. Peter Anvin, Linus Torvalds, Kees Cook, Peter Zijlstra, Josh Poimboeuf, x86, linux-kernel The following commit has been merged into the x86/urgent branch of tip: Commit-ID: 57e2428f8df8263275344566e02c277648a4b7f1 Gitweb: https://git.kernel.org/tip/57e2428f8df8263275344566e02c277648a4b7f1 Author: Jann Horn <jannh@google.com> AuthorDate: Tue, 25 Mar 2025 03:01:22 +01:00 Committer: Ingo Molnar <mingo@kernel.org> CommitterDate: Tue, 25 Mar 2025 08:30:43 +01:00 x86/entry: Fix ORC unwinder for PUSH_REGS with save_ret=1 PUSH_REGS with save_ret=1 is used by interrupt entry helper functions that initially start with a UNWIND_HINT_FUNC ORC state. However, save_ret=1 means that we clobber the helper function's return address (and then later restore the return address further down on the stack); after that point, the only thing on the stack we can unwind through is the IRET frame, so use UNWIND_HINT_IRET_REGS until we have a full pt_regs frame. ( An alternate approach would be to move the pt_regs->di overwrite down such that it is the final step of pt_regs setup; but I don't want to rearrange entry code just to make unwinding a tiny bit more elegant. ) Fixes: 9e809d15d6b6 ("x86/entry: Reduce the code footprint of the 'idtentry' macro") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Brian Gerst <brgerst@gmail.com> Cc: Juergen Gross <jgross@suse.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Kees Cook <keescook@chromium.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Link: https://lore.kernel.org/r/20250325-2025-03-unwind-fixes-v1-1-acd774364768@google.com --- arch/x86/entry/calling.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index cb0911c..d83236b 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h @@ -70,6 +70,8 @@ For 32-bit we have the following conventions - kernel is built with pushq %rsi /* pt_regs->si */ movq 8(%rsp), %rsi /* temporarily store the return address in %rsi */ movq %rdi, 8(%rsp) /* pt_regs->di (overwriting original return address) */ + /* We just clobbered the return address - use the IRET frame for unwinding: */ + UNWIND_HINT_IRET_REGS offset=3*8 .else pushq %rdi /* pt_regs->di */ pushq %rsi /* pt_regs->si */ ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks 2025-03-25 2:01 [PATCH 0/2] x86 unwind fixes (1 unwinder fix, 1 missing entry ORC annotation) Jann Horn 2025-03-25 2:01 ` [PATCH 1/2] x86/entry: Fix ORC for PUSH_REGS with save_ret=1 Jann Horn @ 2025-03-25 2:01 ` Jann Horn 2025-03-25 2:24 ` Jann Horn ` (2 more replies) 1 sibling, 3 replies; 8+ messages in thread From: Jann Horn @ 2025-03-25 2:01 UTC (permalink / raw) To: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra Cc: linux-kernel, Dominik Brodowski, Ingo Molnar, Oleg Nesterov, Vernon Lovejoy, Jann Horn Commit 2e4be0d011f2 ("x86/show_trace_log_lvl: Ensure stack pointer is aligned, again") was intended to ensure alignment of the stack pointer; but it also moved the initialization of the "stack" variable down into the loop header. This was likely intended as a no-op cleanup, since the commit message does not mention it; however, this caused a behavioral change because the value of "regs" is different between the two places. Originally, get_stack_pointer() used the regs provided by the caller; after that commit, get_stack_pointer() instead uses the regs at the top of the stack frame the unwinder is looking at. Often, there are no such regs at all, and "regs" is NULL, causing get_stack_pointer() to fall back to the task's current stack pointer, which is not what we want here, but probably happens to mostly work. Other times, the original regs will point to another regs frame - in that case, the linear guess unwind logic in show_trace_log_lvl() will start unwinding too far up the stack, causing the first frame found by the proper unwinder to never be visited, resulting in a stack trace consisting purely of guess lines. Fix it by moving the "stack = " assignment back where it belongs. Fixes: 2e4be0d011f2 ("x86/show_trace_log_lvl: Ensure stack pointer is aligned, again") Signed-off-by: Jann Horn <jannh@google.com> --- arch/x86/kernel/dumpstack.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index a7d562697e50..b2b118a8c09b 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -195,6 +195,7 @@ static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, printk("%sCall Trace:\n", log_lvl); unwind_start(&state, task, regs, stack); + stack = stack ?: get_stack_pointer(task, regs); regs = unwind_get_entry_regs(&state, &partial); /* @@ -213,9 +214,7 @@ static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, * - hardirq stack * - entry stack */ - for (stack = stack ?: get_stack_pointer(task, regs); - stack; - stack = stack_info.next_sp) { + for (; stack; stack = stack_info.next_sp) { const char *stack_name; stack = PTR_ALIGN(stack, sizeof(long)); -- 2.49.0.395.g12beb8f557-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks 2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn @ 2025-03-25 2:24 ` Jann Horn 2025-03-25 7:31 ` Ingo Molnar 2025-03-25 7:46 ` [tip: x86/urgent] x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment tip-bot2 for Jann Horn 2025-03-25 13:27 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Oleg Nesterov 2 siblings, 1 reply; 8+ messages in thread From: Jann Horn @ 2025-03-25 2:24 UTC (permalink / raw) To: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra Cc: linux-kernel, Dominik Brodowski, Ingo Molnar, Oleg Nesterov, Vernon Lovejoy On Tue, Mar 25, 2025 at 3:01 AM Jann Horn <jannh@google.com> wrote: > Originally, get_stack_pointer() used the regs provided by the caller; after > that commit, get_stack_pointer() instead uses the regs at the top of the > stack frame the unwinder is looking at. Often, there are no such regs at > all, and "regs" is NULL, causing get_stack_pointer() to fall back to the > task's current stack pointer, which is not what we want here, but probably > happens to mostly work. Other times, the original regs will point to > another regs frame - in that case, the linear guess unwind logic in > show_trace_log_lvl() will start unwinding too far up the stack, causing the > first frame found by the proper unwinder to never be visited, resulting in > a stack trace consisting purely of guess lines. I guess the subject line is kind of misleading - maybe "x86/dumpstack: Fix misplaced assignment in unwinder" would be better? ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks 2025-03-25 2:24 ` Jann Horn @ 2025-03-25 7:31 ` Ingo Molnar 0 siblings, 0 replies; 8+ messages in thread From: Ingo Molnar @ 2025-03-25 7:31 UTC (permalink / raw) To: Jann Horn Cc: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra, linux-kernel, Dominik Brodowski, Oleg Nesterov, Vernon Lovejoy * Jann Horn <jannh@google.com> wrote: > On Tue, Mar 25, 2025 at 3:01 AM Jann Horn <jannh@google.com> wrote: > > Originally, get_stack_pointer() used the regs provided by the caller; after > > that commit, get_stack_pointer() instead uses the regs at the top of the > > stack frame the unwinder is looking at. Often, there are no such regs at > > all, and "regs" is NULL, causing get_stack_pointer() to fall back to the > > task's current stack pointer, which is not what we want here, but probably > > happens to mostly work. Other times, the original regs will point to > > another regs frame - in that case, the linear guess unwind logic in > > show_trace_log_lvl() will start unwinding too far up the stack, causing the > > first frame found by the proper unwinder to never be visited, resulting in > > a stack trace consisting purely of guess lines. > > I guess the subject line is kind of misleading - maybe "x86/dumpstack: > Fix misplaced assignment in unwinder" would be better? Well, it's a bug and the code is broken that results in subpar stack dumps from exception contexts that fall back to the guess-dumper, right? So I've edited the subject line to: x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment But I'd have no problem calling it broken either - even if the bug doesn't crash anything. Thanks, Ingo ^ permalink raw reply [flat|nested] 8+ messages in thread
* [tip: x86/urgent] x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment 2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn 2025-03-25 2:24 ` Jann Horn @ 2025-03-25 7:46 ` tip-bot2 for Jann Horn 2025-03-25 13:27 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Oleg Nesterov 2 siblings, 0 replies; 8+ messages in thread From: tip-bot2 for Jann Horn @ 2025-03-25 7:46 UTC (permalink / raw) To: linux-tip-commits; +Cc: Jann Horn, Ingo Molnar, x86, linux-kernel The following commit has been merged into the x86/urgent branch of tip: Commit-ID: 2c118f50d7fd4d9aefc4533a26f83338b2906b7a Gitweb: https://git.kernel.org/tip/2c118f50d7fd4d9aefc4533a26f83338b2906b7a Author: Jann Horn <jannh@google.com> AuthorDate: Tue, 25 Mar 2025 03:01:23 +01:00 Committer: Ingo Molnar <mingo@kernel.org> CommitterDate: Tue, 25 Mar 2025 08:30:43 +01:00 x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment Commit: 2e4be0d011f2 ("x86/show_trace_log_lvl: Ensure stack pointer is aligned, again") was intended to ensure alignment of the stack pointer; but it also moved the initialization of the "stack" variable down into the loop header. This was likely intended as a no-op cleanup, since the commit message does not mention it; however, this caused a behavioral change because the value of "regs" is different between the two places. Originally, get_stack_pointer() used the regs provided by the caller; after that commit, get_stack_pointer() instead uses the regs at the top of the stack frame the unwinder is looking at. Often, there are no such regs at all, and "regs" is NULL, causing get_stack_pointer() to fall back to the task's current stack pointer, which is not what we want here, but probably happens to mostly work. Other times, the original regs will point to another regs frame - in that case, the linear guess unwind logic in show_trace_log_lvl() will start unwinding too far up the stack, causing the first frame found by the proper unwinder to never be visited, resulting in a stack trace consisting purely of guess lines. Fix it by moving the "stack = " assignment back where it belongs. Fixes: 2e4be0d011f2 ("x86/show_trace_log_lvl: Ensure stack pointer is aligned, again") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lore.kernel.org/r/20250325-2025-03-unwind-fixes-v1-2-acd774364768@google.com --- arch/x86/kernel/dumpstack.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 91639d1..c6fefd4 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -195,6 +195,7 @@ static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, printk("%sCall Trace:\n", log_lvl); unwind_start(&state, task, regs, stack); + stack = stack ?: get_stack_pointer(task, regs); regs = unwind_get_entry_regs(&state, &partial); /* @@ -213,9 +214,7 @@ static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, * - hardirq stack * - entry stack */ - for (stack = stack ?: get_stack_pointer(task, regs); - stack; - stack = stack_info.next_sp) { + for (; stack; stack = stack_info.next_sp) { const char *stack_name; stack = PTR_ALIGN(stack, sizeof(long)); ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks 2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn 2025-03-25 2:24 ` Jann Horn 2025-03-25 7:46 ` [tip: x86/urgent] x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment tip-bot2 for Jann Horn @ 2025-03-25 13:27 ` Oleg Nesterov 2 siblings, 0 replies; 8+ messages in thread From: Oleg Nesterov @ 2025-03-25 13:27 UTC (permalink / raw) To: Jann Horn Cc: x86, Andy Lutomirski, Josh Poimboeuf, Peter Zijlstra, linux-kernel, Dominik Brodowski, Ingo Molnar, Vernon Lovejoy On 03/25, Jann Horn wrote: > > Commit 2e4be0d011f2 ("x86/show_trace_log_lvl: Ensure stack pointer is > aligned, again") was intended to ensure alignment of the stack pointer; but > it also moved the initialization of the "stack" variable down into the loop > header. This was likely intended as a no-op cleanup, since the commit > message does not mention it; Yes... initial version didn't do this, this was requested during review and I didn't realize this adds another problem. Thanks Jann! Oleg. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-03-25 13:28 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-03-25 2:01 [PATCH 0/2] x86 unwind fixes (1 unwinder fix, 1 missing entry ORC annotation) Jann Horn 2025-03-25 2:01 ` [PATCH 1/2] x86/entry: Fix ORC for PUSH_REGS with save_ret=1 Jann Horn 2025-03-25 7:46 ` [tip: x86/urgent] x86/entry: Fix ORC unwinder " tip-bot2 for Jann Horn 2025-03-25 2:01 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Jann Horn 2025-03-25 2:24 ` Jann Horn 2025-03-25 7:31 ` Ingo Molnar 2025-03-25 7:46 ` [tip: x86/urgent] x86/dumpstack: Fix inaccurate unwinding from exception stacks due to misplaced assignment tip-bot2 for Jann Horn 2025-03-25 13:27 ` [PATCH 2/2] x86/dumpstack: Fix broken unwinding from exception stacks Oleg Nesterov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox