public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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

* [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

* [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

* 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