* Re: [PATCH] perf/core: Prevent duplicate sigtrap task work enqueue
2026-11-02 16:35 ` Sebastian Andrzej Siewior
@ 2026-11-03 1:21 ` Tengda Wu
0 siblings, 0 replies; 3+ messages in thread
From: Tengda Wu @ 2026-11-03 1:21 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark, linux-perf-users,
linux-kernel
On 2026/11/3 0:35, Sebastian Andrzej Siewior wrote:
> On 2026-10-31 11:45:20 [+0000], Tengda Wu wrote:
>> The sigtrap handling in __perf_event_overflow() checks
>> event->pending_work before calling task_work_add(), but only sets
>> pending_work after the task work has been queued.
>>
>> This leaves a window where an NMI can interrupt task_work_add()
>> and re-enter __perf_event_overflow() for the same event. Since
>> pending_work is still zero, the nested invocation can enqueue
>> the same pending_task again.
>>
>> The re-entrant call path is:
>>
>> __perf_event_overflow()
>> task_work_add()
>> kasan_record_aux_stack()
>> <NMI>
>> __perf_event_overflow()
>> task_work_add()
>
> How was the issue identified?
>
> Sebastian
This issue was discovered by syzkaller, and the warning and the reproducer
(named "gpt", because I used GPT to convert the syzkaller program into C code)
are as follows:
Warning
=======
# ./gpt
msr:write_msr id=2252 cpu=1 workers=4 duration=60s
Each child opens tracepoint(sigtrap) + hardware(cache-misses), then exits without close().
[ 67.536731] gpt (1697) used greatest stack depth: 24952 bytes left
[ 67.686591] ------------[ cut here ]------------
[ 67.686598] WARNING: kernel/events/core.c:10932 at __perf_event_overflow+0xa48/0xbf0, CPU#1: gpt/1709
[ 67.688569] Modules linked in:
[ 67.689017] CPU: 1 UID: 0 PID: 1709 Comm: gpt Not tainted 7.3.0-next-20261027 #2 PREEMPT(lazy)
[ 67.690194] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b3f840-prebuilt.qemu.org 04/4
[ 67.691718] RIP: 0010:__perf_event_overflow+0xa48/0xbf0
[ 67.692431] Code: ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 90 01 00 00 48 8b 04 24 48 8b 80 b8 00 00 00 49 89 86 50 0
[ 67.694915] RSP: 0000:fffffe0000058888 EFLAGS: 00010046
[ 67.695633] RAX: 0000000000000000 RBX: 1fffffc00000b119 RCX: 0000000000000001
[ 67.696594] RDX: 1ffff11023118a5e RSI: 0000000000000008 RDI: ffff8881188c52f0
[ 67.697552] RBP: ffff8881188c51cc R08: 0000000000000001 R09: ffffed1023118a5e
[ 67.698519] R10: ffff8881188c52f7 R11: ffff8881188c5428 R12: ffff8881f70b3538
[ 67.699480] R13: ffff8881188c52f0 R14: ffff8881188c5080 R15: ffff8881188c53c4
[ 67.700445] FS: 00007fed98fc6600(0000) GS:ffff88826ff74000(0000) knlGS:0000000000000000
[ 67.701532] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 67.702317] CR2: 00007fed98d06000 CR3: 000000010507c004 CR4: 0000000000170ef0
[ 67.703281] Call Trace:
[ 67.703636] <NMI>
[ 67.703936] ? __pfx___perf_event_overflow+0x10/0x10
[ 67.704629] ? perf_swevent_set_period+0x131/0x1c0
[ 67.705288] ? __pfx_perf_swevent_set_period+0x10/0x10
[ 67.705992] ? __pfx_perf_event_output_forward+0x10/0x10
[ 67.706726] perf_swevent_event+0x1d0/0x390
[ 67.707304] perf_tp_event+0x3b3/0x8c0
[ 67.707831] ? __perf_event_account_interrupt+0x24/0x570
[ 67.708562] ? __pfx_perf_tp_event+0x10/0x10
[ 67.709158] ? __pfx_x86_pmu_update_perf_regs+0x10/0x10
[ 67.709879] ? handle_pmi_common+0x42f/0x8a0
[ 67.710475] ? __pfx_handle_pmi_common+0x10/0x10
[ 67.711118] perf_trace_msr_trace_class+0x28e/0x450
[ 67.711797] ? __pfx_perf_trace_msr_trace_class+0x10/0x10
[ 67.712532] ? intel_bts_interrupt+0x71/0x4c0
[ 67.713146] do_trace_write_msr+0xaa/0xe0
[ 67.713707] __intel_pmu_enable_all.constprop.0+0x1db/0x350
[ 67.714467] intel_pmu_handle_irq+0x721/0xb70
[ 67.715079] ? __pfx_mce_raise_notify+0x10/0x10
[ 67.715711] ? irq_work_queue+0x7a/0xc0
[ 67.716248] perf_event_nmi_handler+0x13c/0x1a0
[ 67.716885] nmi_handle.part.0+0xb9/0x350
[ 67.717443] ? find_bug+0x3d/0x230
[ 67.717931] default_do_nmi+0x6b/0x180
[ 67.718458] exc_nmi+0xf3/0x120
[ 67.718910] end_repeat_nmi+0xf/0x53
[ 67.719413] RIP: 0010:find_bug+0x3d/0x230
[ 67.719975] Code: 48 89 fb 48 3d b0 65 0d 86 0f 83 8c 00 00 00 48 ba 00 00 00 00 00 fc ff df 48 89 c1 83 e0 07 48 c1 e9 4
[ 67.722448] RSP: 0000:ffff888118957ae0 EFLAGS: 00000006
[ 67.723166] RAX: 0000000000000003 RBX: ffffffff81a666e5 RCX: 1ffffffff0c14c80
[ 67.724129] RDX: 0000000000000000 RSI: ffffffff81a666e5 RDI: ffffffff81a666e5
[ 67.725098] RBP: ffffffff8713f810 R08: 0000000000000000 R09: 0000000000000000
[ 67.726066] R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff81a666e5
[ 67.727033] R13: ffff888118957c88 R14: 0000000000000000 R15: 0000000000000000
[ 67.728000] ? put_pmu_ctx+0x255/0x300
[ 67.728528] ? put_pmu_ctx+0x255/0x300
[ 67.729056] ? put_pmu_ctx+0x255/0x300
[ 67.729583] ? put_pmu_ctx+0x255/0x300
[ 67.730111] ? find_bug+0x3d/0x230
[ 67.730597] ? find_bug+0x3d/0x230
[ 67.731077] </NMI>
[ 67.731383] <TASK>
[ 67.731694] ? put_pmu_ctx+0x255/0x300
[ 67.732217] __report_bug+0x35a/0x3e0
[ 67.732736] ? put_pmu_ctx+0x255/0x300
[ 67.733261] ? __kasan_slab_free+0x43/0x70
[ 67.733834] ? __pfx___report_bug+0x10/0x10
[ 67.734411] ? task_work_run+0x13d/0x200
[ 67.734962] ? irqentry_exit+0x3a8/0x700
[ 67.735507] ? asm_exc_page_fault+0x26/0x30
[ 67.736090] ? __send_signal_locked+0x3c0/0xb40
[ 67.736726] ? __send_signal_locked+0x52f/0xb40
[ 67.737358] ? put_pmu_ctx+0x255/0x300
[ 67.737886] report_bug+0x95/0x1a0
[ 67.738368] ? put_pmu_ctx+0x255/0x300
[ 67.738897] handle_bug+0x267/0x2d0
[ 67.739387] exc_invalid_op+0x18/0x50
[ 67.739904] asm_exc_invalid_op+0x1a/0x20
[ 67.740459] RIP: 0010:put_pmu_ctx+0x255/0x300
[ 67.741067] Code: 75 4a 48 83 c4 68 5b 5d 41 5c 41 5d 41 5e 41 5f e9 50 8b d4 02 48 c7 c6 80 57 a6 81 e8 e4 af b1 ff eb c
[ 67.743549] RSP: 0000:ffff888118957d30 EFLAGS: 00010083
[ 67.744268] RAX: ffff8881188c50a0 RBX: ffff888104258f00 RCX: 1ffff110209df084
[ 67.745235] RDX: 1ffff1102084b1e4 RSI: 0000000000000004 RDI: ffff888107949218
[ 67.746201] RBP: 1ffff1102312afa7 R08: 0000000000000001 R09: ffffed102084b1ea
[ 67.747167] R10: ffff888104258f53 R11: ffffffff810012a6 R12: ffff888104258f20
[ 67.748132] R13: ffff888104258f08 R14: ffff888104ef8400 R15: ffff888104ef8420
[ 67.749098] ? asm_exc_page_fault+0x26/0x30
[ 67.749680] ? __pfx_put_pmu_ctx+0x10/0x10
[ 67.750246] ? mutex_unlock+0x82/0xd0
[ 67.750763] ? __pfx_mutex_unlock+0x10/0x10
[ 67.751342] __free_event+0x1cf/0x680
[ 67.751855] ? _raw_spin_lock_irq+0x8b/0xe0
[ 67.752436] perf_pending_task+0x101/0x330
[ 67.753009] ? count_memcg_events+0x273/0x410
[ 67.753620] task_work_run+0x13d/0x200
[ 67.754146] ? __pfx_task_work_run+0x10/0x10
[ 67.754744] ? __do_sys_rt_sigreturn+0x161/0x1b0
[ 67.755385] irqentry_exit+0x3a8/0x700
[ 67.755912] asm_exc_page_fault+0x26/0x30
[ 67.756467] RIP: 0033:0x7fed98f3cd3a
[ 67.756979] Code: 0f 82 0a 01 00 00 48 83 fa 40 77 7d c5 fe 7f 44 17 e0 c5 fe 7f 07 c5 f8 77 c3 66 0f 1f 44 00 00 48 89 0
[ 67.759451] RSP: 002b:00007ffdb7dc7628 EFLAGS: 00010206
[ 67.760168] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000119010
[ 67.761133] RDX: 00007fed985c7010 RSI: 0000000000000000 RDI: 00007fed98cae000
[ 67.762101] RBP: 00007ffdb7dc7690 R08: 00007fed985c7010 R09: 0000000000000000
[ 67.763069] R10: 0000000000000022 R11: 0000000000000246 R12: 00007ffdb7dc7918
[ 67.764033] R13: 0000000000401976 R14: 00007fed98fffaa0 R15: 0000000000403e08
[ 67.765001] </TASK>
[ 67.765320] Kernel panic - not syncing: kernel: panic_on_warn set ...
[ 67.766196] CPU: 1 UID: 0 PID: 1709 Comm: gpt Not tainted 7.3.0-next-20261027 #2 PREEMPT(lazy)
[ 67.767370] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b3f840-prebuilt.qemu.org 04/4
[ 67.768902] Call Trace:
[ 67.769254] <NMI>
[ 67.769551] vpanic+0x4be/0x540
[ 67.770012] ? __perf_event_overflow+0xa48/0xbf0
[ 67.770653] panic+0xc2/0xd0
[ 67.771068] ? __pfx_panic+0x10/0x10
[ 67.771575] check_panic_on_warn+0x7e/0x80
[ 67.772145] __warn+0xef/0x2b0
[ 67.772586] __report_bug+0x312/0x3e0
[ 67.773102] ? __perf_event_overflow+0xa48/0xbf0
[ 67.773743] ? __pfx___report_bug+0x10/0x10
[ 67.774323] ? perf_tp_event+0x3b3/0x8c0
[ 67.774872] ? arch_irq_work_raise+0x3f/0x60
[ 67.775467] ? __irq_work_queue_local+0x16b/0x3d0
[ 67.776117] ? __perf_event_overflow+0xa48/0xbf0
[ 67.776759] report_bug+0x95/0x1a0
[ 67.777241] ? __perf_event_overflow+0xa48/0xbf0
[ 67.777883] handle_bug+0x267/0x2d0
[ 67.778375] exc_invalid_op+0x18/0x50
[ 67.778895] asm_exc_invalid_op+0x1a/0x20
[ 67.779451] RIP: 0010:__perf_event_overflow+0xa48/0xbf0
[ 67.780169] Code: ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 90 01 00 00 48 8b 04 24 48 8b 80 b8 00 00 00 49 89 86 50 0
[ 67.782645] RSP: 0000:fffffe0000058888 EFLAGS: 00010046
[ 67.783359] RAX: 0000000000000000 RBX: 1fffffc00000b119 RCX: 0000000000000001
[ 67.784326] RDX: 1ffff11023118a5e RSI: 0000000000000008 RDI: ffff8881188c52f0
[ 67.785292] RBP: ffff8881188c51cc R08: 0000000000000001 R09: ffffed1023118a5e
[ 67.786258] R10: ffff8881188c52f7 R11: ffff8881188c5428 R12: ffff8881f70b3538
[ 67.787223] R13: ffff8881188c52f0 R14: ffff8881188c5080 R15: ffff8881188c53c4
[ 67.788191] ? __perf_event_overflow+0x975/0xbf0
[ 67.788833] ? __pfx___perf_event_overflow+0x10/0x10
[ 67.789514] ? perf_swevent_set_period+0x131/0x1c0
[ 67.790179] ? __pfx_perf_swevent_set_period+0x10/0x10
[ 67.790888] ? __pfx_perf_event_output_forward+0x10/0x10
[ 67.791618] perf_swevent_event+0x1d0/0x390
[ 67.792197] perf_tp_event+0x3b3/0x8c0
[ 67.792726] ? __perf_event_account_interrupt+0x24/0x570
[ 67.793453] ? __pfx_perf_tp_event+0x10/0x10
[ 67.794051] ? __pfx_x86_pmu_update_perf_regs+0x10/0x10
[ 67.794770] ? handle_pmi_common+0x42f/0x8a0
[ 67.795366] ? __pfx_handle_pmi_common+0x10/0x10
[ 67.796009] perf_trace_msr_trace_class+0x28e/0x450
[ 67.796686] ? __pfx_perf_trace_msr_trace_class+0x10/0x10
[ 67.797424] ? intel_bts_interrupt+0x71/0x4c0
[ 67.798035] do_trace_write_msr+0xaa/0xe0
[ 67.798596] __intel_pmu_enable_all.constprop.0+0x1db/0x350
[ 67.799357] intel_pmu_handle_irq+0x721/0xb70
[ 67.799965] ? __pfx_mce_raise_notify+0x10/0x10
[ 67.800600] ? irq_work_queue+0x7a/0xc0
[ 67.801136] perf_event_nmi_handler+0x13c/0x1a0
[ 67.801770] nmi_handle.part.0+0xb9/0x350
[ 67.802329] ? find_bug+0x3d/0x230
[ 67.802816] default_do_nmi+0x6b/0x180
[ 67.803343] exc_nmi+0xf3/0x120
[ 67.803795] end_repeat_nmi+0xf/0x53
[ 67.804298] RIP: 0010:find_bug+0x3d/0x230
[ 67.804858] Code: 48 89 fb 48 3d b0 65 0d 86 0f 83 8c 00 00 00 48 ba 00 00 00 00 00 fc ff df 48 89 c1 83 e0 07 48 c1 e9 4
[ 67.807329] RSP: 0000:ffff888118957ae0 EFLAGS: 00000006
[ 67.808047] RAX: 0000000000000003 RBX: ffffffff81a666e5 RCX: 1ffffffff0c14c80
[ 67.809012] RDX: 0000000000000000 RSI: ffffffff81a666e5 RDI: ffffffff81a666e5
[ 67.809978] RBP: ffffffff8713f810 R08: 0000000000000000 R09: 0000000000000000
[ 67.810943] R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff81a666e5
[ 67.811909] R13: ffff888118957c88 R14: 0000000000000000 R15: 0000000000000000
[ 67.812877] ? put_pmu_ctx+0x255/0x300
[ 67.813401] ? put_pmu_ctx+0x255/0x300
[ 67.813930] ? put_pmu_ctx+0x255/0x300
[ 67.814453] ? put_pmu_ctx+0x255/0x300
[ 67.814984] ? find_bug+0x3d/0x230
[ 67.815465] ? find_bug+0x3d/0x230
[ 67.815948] </NMI>
[ 67.816255] <TASK>
[ 67.816567] ? put_pmu_ctx+0x255/0x300
[ 67.817092] __report_bug+0x35a/0x3e0
[ 67.817610] ? put_pmu_ctx+0x255/0x300
[ 67.818134] ? __kasan_slab_free+0x43/0x70
[ 67.818705] ? __pfx___report_bug+0x10/0x10
[ 67.819283] ? task_work_run+0x13d/0x200
[ 67.819833] ? irqentry_exit+0x3a8/0x700
[ 67.820378] ? asm_exc_page_fault+0x26/0x30
[ 67.820959] ? __send_signal_locked+0x3c0/0xb40
[ 67.821592] ? __send_signal_locked+0x52f/0xb40
[ 67.822221] ? put_pmu_ctx+0x255/0x300
[ 67.822749] report_bug+0x95/0x1a0
[ 67.823230] ? put_pmu_ctx+0x255/0x300
[ 67.823759] handle_bug+0x267/0x2d0
[ 67.824250] exc_invalid_op+0x18/0x50
[ 67.824766] asm_exc_invalid_op+0x1a/0x20
[ 67.825322] RIP: 0010:put_pmu_ctx+0x255/0x300
[ 67.825932] Code: 75 4a 48 83 c4 68 5b 5d 41 5c 41 5d 41 5e 41 5f e9 50 8b d4 02 48 c7 c6 80 57 a6 81 e8 e4 af b1 ff eb c
[ 67.828401] RSP: 0000:ffff888118957d30 EFLAGS: 00010083
[ 67.829117] RAX: ffff8881188c50a0 RBX: ffff888104258f00 RCX: 1ffff110209df084
[ 67.830083] RDX: 1ffff1102084b1e4 RSI: 0000000000000004 RDI: ffff888107949218
[ 67.831046] RBP: 1ffff1102312afa7 R08: 0000000000000001 R09: ffffed102084b1ea
[ 67.832011] R10: ffff888104258f53 R11: ffffffff810012a6 R12: ffff888104258f20
[ 67.832978] R13: ffff888104258f08 R14: ffff888104ef8400 R15: ffff888104ef8420
[ 67.833948] ? asm_exc_page_fault+0x26/0x30
[ 67.834526] ? __pfx_put_pmu_ctx+0x10/0x10
[ 67.835097] ? mutex_unlock+0x82/0xd0
[ 67.835613] ? __pfx_mutex_unlock+0x10/0x10
[ 67.836192] __free_event+0x1cf/0x680
[ 67.836707] ? _raw_spin_lock_irq+0x8b/0xe0
[ 67.837289] perf_pending_task+0x101/0x330
[ 67.837862] ? count_memcg_events+0x273/0x410
[ 67.838470] task_work_run+0x13d/0x200
[ 67.839000] ? __pfx_task_work_run+0x10/0x10
[ 67.839599] ? __do_sys_rt_sigreturn+0x161/0x1b0
[ 67.840238] irqentry_exit+0x3a8/0x700
[ 67.840767] asm_exc_page_fault+0x26/0x30
[ 67.841323] RIP: 0033:0x7fed98f3cd3a
[ 67.841827] Code: 0f 82 0a 01 00 00 48 83 fa 40 77 7d c5 fe 7f 44 17 e0 c5 fe 7f 07 c5 f8 77 c3 66 0f 1f 44 00 00 48 89 0
[ 67.844298] RSP: 002b:00007ffdb7dc7628 EFLAGS: 00010206
[ 67.845015] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000119010
[ 67.845980] RDX: 00007fed985c7010 RSI: 0000000000000000 RDI: 00007fed98cae000
[ 67.846945] RBP: 00007ffdb7dc7690 R08: 00007fed985c7010 R09: 0000000000000000
[ 67.847909] R10: 0000000000000022 R11: 0000000000000246 R12: 00007ffdb7dc7918
[ 67.848876] R13: 0000000000401976 R14: 00007fed98fffaa0 R15: 0000000000403e08
[ 67.849845] </TASK>
[ 67.850285] Dumping ftrace buffer:
[ 67.850779] (ftrace buffer empty)
[ 67.851279] Kernel Offset: disabled
[ 67.851772] ---[ end Kernel panic - not syncing: kernel: panic_on_warn set ... ]---
Reproducer (named "gpt")
========================
/*
* Reproducer for the perf_trace_event_unreg -> _free_event -> put_pmu_ctx()
* path reached from perf_pending_task() on Linux 6.6 x86.
*
* Run only in a disposable VM with the Intel PMU exposed to the guest.
* The test intentionally exits short-lived children without explicitly
* closing the perf fds: exit_files() tears them down, then exit task_work
* can drop the pending reference held by perf_pending_task().
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include "perf_event.h"
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static volatile sig_atomic_t sigtrap_count;
static void sigtrap_handler(int sig, siginfo_t *info, void *ctx)
{
(void)sig;
(void)info;
(void)ctx;
sigtrap_count++;
}
static int perf_open(struct perf_event_attr *attr, pid_t pid, int cpu)
{
return syscall(__NR_perf_event_open, attr, pid, cpu, -1,
PERF_FLAG_FD_CLOEXEC);
}
static int read_msr_tracepoint_id(void)
{
static const char * const paths[] = {
"/sys/kernel/tracing/events/msr/write_msr/id",
"/sys/kernel/debug/tracing/events/msr/write_msr/id",
};
char buf[64];
unsigned int i;
for (i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
int fd = open(paths[i], O_RDONLY | O_CLOEXEC);
ssize_t n;
if (fd < 0)
continue;
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n <= 0)
continue;
buf[n] = '\0';
return (int)strtol(buf, NULL, 10);
}
return -1;
}
static int open_msr_sigtrap_event(int tracepoint_id, int cpu)
{
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.type = PERF_TYPE_TRACEPOINT;
attr.config = tracepoint_id;
attr.sample_period = 4;
attr.sample_type = PERF_SAMPLE_IP;
attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
attr.pinned = 1;
attr.exclusive = 1;
attr.exclude_idle = 1;
attr.exclude_user = 0;
attr.exclude_kernel = 0;
attr.remove_on_exec = 1;
attr.sigtrap = 1;
attr.sig_data = 7;
/* pid=0 makes this a current-task event constrained to @cpu. */
return perf_open(&attr, 0, cpu);
}
static int open_cache_miss_event(int cpu)
{
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CACHE_MISSES; /* original config == 3 */
attr.sample_period = 100;
attr.sample_type = PERF_SAMPLE_IP;
attr.pinned = 1;
attr.exclusive = 1;
attr.exclude_idle = 1;
attr.exclude_user = 0;
attr.exclude_kernel = 0;
/* Do not set sigtrap here: the tracepoint event must own pending_task. */
return perf_open(&attr, 0, cpu);
}
static int set_cpu(int cpu)
{
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
return sched_setaffinity(0, sizeof(set), &set);
}
static void workload(unsigned int ms)
{
struct timespec start, now;
uint64_t elapsed;
const size_t bytes = 8 * 1024 * 1024;
volatile unsigned char *buf = malloc(bytes);
volatile uint64_t x = 1;
uint32_t index = 0x12345678;
if (buf)
memset((void *)buf, 0, bytes);
clock_gettime(CLOCK_MONOTONIC, &start);
do {
for (unsigned int i = 0; i < 100000; i++) {
x = x * 1664525 + 1013904223;
/* Pseudo-random cache-line accesses over an 8 MiB set. */
index = index * 1664525 + 1013904223;
if (buf)
buf[(index & (bytes - 1)) & ~63U]++;
}
clock_gettime(CLOCK_MONOTONIC, &now);
elapsed = (uint64_t)(now.tv_sec - start.tv_sec) * 1000;
if (now.tv_nsec >= start.tv_nsec)
elapsed += (uint64_t)(now.tv_nsec - start.tv_nsec) / 1000000;
else
elapsed -= (uint64_t)(start.tv_nsec - now.tv_nsec) / 1000000;
} while (elapsed < ms);
free((void *)buf);
(void)x;
}
/* One short-lived task, matching syzkaller's fork/execute/exit pattern. */
static void one_child(int cpu, int tracepoint_id)
{
struct sigaction sa;
int tp_fd, hw_fd;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigtrap_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGTRAP, &sa, NULL))
_exit(10);
/* As in the original POC, constrain the task after opening events. */
if (set_cpu(cpu)) {
dprintf(2, "[%d] sched_setaffinity failed: %s\n",
getpid(), strerror(errno));
_exit(13);
}
/*
* Open the hardware event first (lower fd), then the tracepoint event
* (higher fd). Linux 6.6 close_files() walks fds upward during exit:
*
* 1. hardware fd closes first and PMU teardown writes MSRs;
* 2. msr:write_msr is still subscribed and can queue task_work;
* 3. tracepoint fd closes, dropping its file reference;
* 4. exit_task_work() runs perf_pending_task(); put_event() can now
* be the final reference and reach perf_trace_event_unreg().
*
* This ordering is intentional and is the main difference from the
* earlier reproducer.
*/
hw_fd = open_cache_miss_event(cpu);
if (hw_fd < 0) {
dprintf(2, "[%d] open cache-misses failed: %s\n",
getpid(), strerror(errno));
_exit(11);
}
tp_fd = open_msr_sigtrap_event(tracepoint_id, cpu);
if (tp_fd < 0) {
dprintf(2, "[%d] open msr tracepoint failed: %s\n",
getpid(), strerror(errno));
_exit(12);
}
workload(5);
/* Do not close either fd here. Exit path ordering is part of the test. */
_exit(0);
}
static void worker(int cpu, int tracepoint_id, time_t end)
{
unsigned long iter = 0;
while (time(NULL) < end) {
pid_t pid = fork();
int status;
if (pid < 0) {
perror("fork");
_exit(2);
}
if (!pid)
one_child(cpu, tracepoint_id);
while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
;
iter++;
}
dprintf(2, "[worker %d] cpu=%d iterations=%lu\n",
getpid(), cpu, iter);
_exit(0);
}
int main(int argc, char **argv)
{
int cpu = argc > 1 ? atoi(argv[1]) : 1;
int workers = argc > 2 ? atoi(argv[2]) : 4;
int duration = argc > 3 ? atoi(argv[3]) : 60;
int tracepoint_id, ncpu;
pid_t *pids;
time_t end;
if (workers < 1)
workers = 1;
if (duration < 1)
duration = 1;
ncpu = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cpu < 0 || cpu >= ncpu) {
fprintf(stderr, "invalid cpu %d (online cpus: %d)\n", cpu, ncpu);
return 1;
}
tracepoint_id = read_msr_tracepoint_id();
if (tracepoint_id < 0) {
fprintf(stderr, "cannot read msr:write_msr tracepoint id; "
"mount tracefs/debugfs and check CONFIG_X86_MSR\n");
return 1;
}
fprintf(stderr, "msr:write_msr id=%d cpu=%d workers=%d duration=%ds\n",
tracepoint_id, cpu, workers, duration);
fprintf(stderr, "Each child opens tracepoint(sigtrap) + "
"hardware(cache-misses), then exits without close().\n");
pids = calloc((size_t)workers, sizeof(*pids));
if (!pids)
return 1;
end = time(NULL) + duration;
for (int i = 0; i < workers; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("fork worker");
break;
}
if (!pid)
worker(cpu, tracepoint_id, end);
pids[i] = pid;
}
for (int i = 0; i < workers; i++)
if (pids[i] > 0)
waitpid(pids[i], NULL, 0);
free(pids);
return 0;
}
Thanks,
Tengda
^ permalink raw reply [flat|nested] 3+ messages in thread