[PATCH] perf/core: Fix WARN in perf_sigtrap()

Tetsuo Handa posted 1 patch 3 months ago
There is a newer version of this series
kernel/events/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
[PATCH] perf/core: Fix WARN in perf_sigtrap()
Posted by Tetsuo Handa 3 months ago
Since commit 4f6fc7821283 ("perf: Fix sample vs do_exit()") has moved
perf_event_exit_task() call from after exit_task_work() to before
exit_task_work(), task_work_add() from perf_event_exit_task() now returns
0 than -ESRCH, despite perf_event_exit_task_context() updates ctx->task
to TASK_TOMBSTONE. As a result, event->ctx->task == current assumption
no longer holds.

Reported-by: syzbot <syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=2fe61cb2a86066be6985
Fixes: 4f6fc7821283 ("perf: Fix sample vs do_exit()")
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
#syz test

 kernel/events/core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7281230044d0..489f42defe3c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7208,7 +7208,8 @@ static void perf_sigtrap(struct perf_event *event)
 	 * ctx->task or current has changed in the meantime. This can be the
 	 * case on architectures that do not implement arch_irq_work_raise().
 	 */
-	if (WARN_ON_ONCE(event->ctx->task != current))
+	if (WARN_ON_ONCE(event->ctx->task != current &&
+			 event->ctx->task != TASK_TOMBSTONE))
 		return;
 
 	/*
-- 
2.50.0
Re: [PATCH] perf/core: Fix WARN in perf_sigtrap()
Posted by Peter Zijlstra 3 months ago
On Sat, Jul 05, 2025 at 11:43:37PM +0900, Tetsuo Handa wrote:
> Since commit 4f6fc7821283 ("perf: Fix sample vs do_exit()") has moved
> perf_event_exit_task() call from after exit_task_work() to before
> exit_task_work(), 

> task_work_add() from perf_event_exit_task() now returns

There is no task_work_add() in perf_event_exit_task().

> 0 than -ESRCH, despite perf_event_exit_task_context() updates ctx->task
> to TASK_TOMBSTONE. As a result, event->ctx->task == current assumption
> no longer holds.

This changelog is confusing to the point that I've no idea what it is
trying to tell me.


Did you mean to say something like:

Because exit_task_work() now runs after perf_event_exit_task(), it is
possible for an already queued perf_pending_task()->perf_sigtrap() to
observe a dead task context.

> Reported-by: syzbot <syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com>
> Closes: https://syzkaller.appspot.com/bug?extid=2fe61cb2a86066be6985
> Fixes: 4f6fc7821283 ("perf: Fix sample vs do_exit()")
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> #syz test
> 
>  kernel/events/core.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 7281230044d0..489f42defe3c 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -7208,7 +7208,8 @@ static void perf_sigtrap(struct perf_event *event)
>  	 * ctx->task or current has changed in the meantime. This can be the
>  	 * case on architectures that do not implement arch_irq_work_raise().
>  	 */
> -	if (WARN_ON_ONCE(event->ctx->task != current))
> +	if (WARN_ON_ONCE(event->ctx->task != current &&
> +			 event->ctx->task != TASK_TOMBSTONE))
>  		return;
>  

Also, isn't it better to simply swap the early exit tests in that
function like so:


diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0db36b2b2448..22fdf0c187cd 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7203,6 +7203,13 @@ void perf_event_wakeup(struct perf_event *event)
 
 static void perf_sigtrap(struct perf_event *event)
 {
+	/*
+	 * Both perf_pending_task() and perf_pending_irq() can race with the
+	 * task exiting.
+	 */
+	if (current->flags & PF_EXITING)
+		return;
+
 	/*
 	 * We'd expect this to only occur if the irq_work is delayed and either
 	 * ctx->task or current has changed in the meantime. This can be the
@@ -7211,13 +7218,6 @@ static void perf_sigtrap(struct perf_event *event)
 	if (WARN_ON_ONCE(event->ctx->task != current))
 		return;
 
-	/*
-	 * Both perf_pending_task() and perf_pending_irq() can race with the
-	 * task exiting.
-	 */
-	if (current->flags & PF_EXITING)
-		return;
-
 	send_sig_perf((void __user *)event->pending_addr,
 		      event->orig_type, event->attr.sig_data);
 }
Re: [PATCH] perf/core: Fix WARN in perf_sigtrap()
Posted by Tetsuo Handa 3 months ago
On 2025/07/09 18:44, Peter Zijlstra wrote:
> On Sat, Jul 05, 2025 at 11:43:37PM +0900, Tetsuo Handa wrote:
>> Since commit 4f6fc7821283 ("perf: Fix sample vs do_exit()") has moved
>> perf_event_exit_task() call from after exit_task_work() to before
>> exit_task_work(), 
> 
>> task_work_add() from perf_event_exit_task() now returns
> 
> There is no task_work_add() in perf_event_exit_task().

Since the function which triggers BUG_ON() is perf_sigtrap(), I guessed
that the location that queued the task work is task_work_add() in
__perf_event_overflow(). But since testing again with

--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -10324,6 +10324,7 @@ static int __perf_event_overflow(struct perf_event *event,
 
                if (!event->pending_work &&
                    !task_work_add(current, &event->pending_task, notify_mode)) {
+                       BUG_ON(event->ctx && event->ctx->task == TASK_TOMBSTONE);
                        event->pending_work = pending_id;
                        local_inc(&event->ctx->nr_no_switch_fast);
                        WARN_ON_ONCE(!atomic_long_inc_not_zero(&event->refcount));

did not trigger, it seems that __perf_event_overflow() is not called from
perf_event_exit_task() path.



> Did you mean to say something like:
> 
> Because exit_task_work() now runs after perf_event_exit_task(), it is
> possible for an already queued perf_pending_task()->perf_sigtrap() to
> observe a dead task context.

Yes.



> Also, isn't it better to simply swap the early exit tests in that
> function like so:

Yes, that will be OK if you prefer it
( https://lkml.kernel.org/r/ed888189-dad4-47e1-bfc8-4f2213eda32d@I-love.SAKURA.ne.jp ).
[PATCH v2] perf/core: Fix WARN in perf_sigtrap()
Posted by Tetsuo Handa 3 months ago
Since exit_task_work() runs after perf_event_exit_task_context() updated
ctx->task to TASK_TOMBSTONE, perf_sigtrap() from perf_pending_task() might
observe event->ctx->task == TASK_TOMBSTONE.

Swap the early exit tests in order not to hit WARN_ON_ONCE().

Reported-by: syzbot <syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=2fe61cb2a86066be6985
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 kernel/events/core.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0db36b2b2448..22fdf0c187cd 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7204,18 +7204,18 @@ void perf_event_wakeup(struct perf_event *event)
 static void perf_sigtrap(struct perf_event *event)
 {
 	/*
-	 * We'd expect this to only occur if the irq_work is delayed and either
-	 * ctx->task or current has changed in the meantime. This can be the
-	 * case on architectures that do not implement arch_irq_work_raise().
+	 * Both perf_pending_task() and perf_pending_irq() can race with the
+	 * task exiting.
 	 */
-	if (WARN_ON_ONCE(event->ctx->task != current))
+	if (current->flags & PF_EXITING)
 		return;
 
 	/*
-	 * Both perf_pending_task() and perf_pending_irq() can race with the
-	 * task exiting.
+	 * We'd expect this to only occur if the irq_work is delayed and either
+	 * ctx->task or current has changed in the meantime. This can be the
+	 * case on architectures that do not implement arch_irq_work_raise().
 	 */
-	if (current->flags & PF_EXITING)
+	if (WARN_ON_ONCE(event->ctx->task != current))
 		return;
 
 	send_sig_perf((void __user *)event->pending_addr,
-- 
2.47.1
[tip: perf/urgent] perf/core: Fix WARN in perf_sigtrap()
Posted by tip-bot2 for Tetsuo Handa 2 months, 4 weeks ago
The following commit has been merged into the perf/urgent branch of tip:

Commit-ID:     3da6bb419750f3ad834786d6ba7c9d5d062c770b
Gitweb:        https://git.kernel.org/tip/3da6bb419750f3ad834786d6ba7c9d5d062c770b
Author:        Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
AuthorDate:    Wed, 09 Jul 2025 20:27:52 +09:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 09 Jul 2025 13:40:17 +02:00

perf/core: Fix WARN in perf_sigtrap()

Since exit_task_work() runs after perf_event_exit_task_context() updated
ctx->task to TASK_TOMBSTONE, perf_sigtrap() from perf_pending_task() might
observe event->ctx->task == TASK_TOMBSTONE.

Swap the early exit tests in order not to hit WARN_ON_ONCE().

Closes: https://syzkaller.appspot.com/bug?extid=2fe61cb2a86066be6985
Reported-by: syzbot <syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/b1c224bd-97f9-462c-a3e3-125d5e19c983@I-love.SAKURA.ne.jp
---
 kernel/events/core.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0db36b2..22fdf0c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7204,18 +7204,18 @@ void perf_event_wakeup(struct perf_event *event)
 static void perf_sigtrap(struct perf_event *event)
 {
 	/*
-	 * We'd expect this to only occur if the irq_work is delayed and either
-	 * ctx->task or current has changed in the meantime. This can be the
-	 * case on architectures that do not implement arch_irq_work_raise().
+	 * Both perf_pending_task() and perf_pending_irq() can race with the
+	 * task exiting.
 	 */
-	if (WARN_ON_ONCE(event->ctx->task != current))
+	if (current->flags & PF_EXITING)
 		return;
 
 	/*
-	 * Both perf_pending_task() and perf_pending_irq() can race with the
-	 * task exiting.
+	 * We'd expect this to only occur if the irq_work is delayed and either
+	 * ctx->task or current has changed in the meantime. This can be the
+	 * case on architectures that do not implement arch_irq_work_raise().
 	 */
-	if (current->flags & PF_EXITING)
+	if (WARN_ON_ONCE(event->ctx->task != current))
 		return;
 
 	send_sig_perf((void __user *)event->pending_addr,
Re: [syzbot] [perf?] WARNING in perf_pending_task
Posted by syzbot 3 months ago
Hello,

syzbot has tested the proposed patch and the reproducer did not trigger any issue:

Reported-by: syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com
Tested-by: syzbot+2fe61cb2a86066be6985@syzkaller.appspotmail.com

Tested on:

commit:         a79a588f Merge tag 'pm-6.16-rc5' of git://git.kernel.o..
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=13691c8c580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=a6dba31fc9bb876c
dashboard link: https://syzkaller.appspot.com/bug?extid=2fe61cb2a86066be6985
compiler:       Debian clang version 20.1.7 (++20250616065708+6146a88f6049-1~exp1~20250616065826.132), Debian LLD 20.1.7
patch:          https://syzkaller.appspot.com/x/patch.diff?x=157a2582580000

Note: testing is done by a robot and is best-effort only.