syzbot
general protection fault in relay_blktrace_event
Status:
moderation: reported on 2026/11/13 15:54
Subsystems:
trace
block
Labels:
prio:low
[Documentation on labels]
Reported-by: syzbot+06e502cccb27b4f6f6c9@syzkaller.appspotmail.com
First crash: 2h22m, last: 2h22m
β¨ AI-generated root cause summary:
In relay_open(), per-CPU channel buffers are only allocated for CPUs currently present in cpu_online_mask under relay_channels_mutex, without holding cpus_read_lock(). Because relay_prepare_cpu() is registered at CPUHP_RELAY_PREPAREβwhich runs in the offline prepare phase prior to CPUHP_BRINGUP_CPU and setting the CPU onlineβa CPU hotplug bringup racing with relay_open() can miss both relay_prepare_cpu() (channel not yet in relay_channels list) and relay_open() (CPU not yet marked online). As a result, the newly online CPU has a NULL per-CPU buffer pointer (*per_cpu_ptr(chan->buf, cpu) == NULL), and relay_reserve() unconditionally dereferences buf->offset without verifying that buf is non-NULL, triggering a kernel NULL-pointer dereference.
▶
▼
β¨ Full AI root cause analysis
Root Cause Analysis
Involved Functions, Structs, and Files
- Files:
- Functions & Callbacks:
- Data Structures & Fields:
struct rchan (chan->buf, chan->list)
struct rchan_buf (buf->offset, buf->chan)
Technical Mechanism & Failure Chain
-
Relay Buffer Allocation vs. CPU Hotplug Lifecycle:
- When
relay_open() creates a relay channel, it iterates over all online CPUs using for_each_online_cpu(i) under relay_channels_mutex:
mutex_lock(&relay_channels_mutex);
for_each_online_cpu(i) {
buf = relay_open_buf(chan, i);
if (!buf)
goto free_bufs;
*per_cpu_ptr(chan->buf, i) = buf;
}
list_add(&chan->list, &relay_channels);
mutex_unlock(&relay_channels_mutex);
Crucially, relay_open() does not acquire cpus_read_lock().
- For CPUs brought online later, the relay subsystem relies on its hotplug state machine callback
relay_prepare_cpu() registered at state CPUHP_RELAY_PREPARE (kernel/cpu.c:2080).
- In the CPU hotplug state machine,
CPUHP_RELAY_PREPARE runs in the early, offline prepare stage well before CPUHP_BRINGUP_CPU kicks the AP alive and marks it present in cpu_online_mask.
-
Race Condition Leading to Unallocated Per-CPU Buffer:
- If a CPU (e.g., CPU 1) hotplug bringup races with channel creation:
- CPU 1 executes
CPUHP_RELAY_PREPARE (relay_prepare_cpu(1)). At this moment, the new channel chan has not yet been added to &relay_channels, so no buffer is created for CPU 1 on this channel.
- CPU 1 proceeds towards online state, but before it is added to
cpu_online_mask, relay_open() executes for_each_online_cpu(i). Since CPU 1 is not yet marked online, relay_open() skips CPU 1, leaving *per_cpu_ptr(chan->buf, 1) == NULL.
relay_open() adds chan to relay_channels and completes.
- CPU 1 finishes bringup and becomes online (
cpu_online(1) == true). Because CPUHP_RELAY_PREPARE already ran during the prepare phase, it will not run again for CPU 1. (A similar condition occurs if CPU 1 was parked at an intermediate state between CPUHP_RELAY_PREPARE and CPUHP_ONLINE).
- Consequently, CPU 1 is fully active and scheduled on, but
*per_cpu_ptr(chan->buf, 1) remains NULL.
-
Crash in relay_reserve():
Sample crash report:
loop2: detected capacity change from 0 to 32768
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000002: 0000 [#1] SMP KASAN PTI
KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
CPU: 1 UID: 0 PID: 13807 Comm: syz.2.2033 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
RIP: 0010:relay_reserve include/linux/relay.h:258 [inline]
RIP: 0010:relay_blktrace_event1 kernel/trace/blktrace.c:124 [inline]
RIP: 0010:relay_blktrace_event+0x34f/0x6e0 kernel/trace/blktrace.c:165
Code: d8 48 c1 e8 03 42 80 3c 28 00 74 08 48 89 df e8 e7 c6 76 00 4b 8d 2c 27 48 83 c5 30 48 8b 1b 4c 8d 63 10 4d 89 e6 49 c1 ee 03 <43> 80 3c 2e 00 74 08 4c 89 e7 e8 c2 c6 76 00 4c 8b 7b 10 49 01 ef
RSP: 0018:ffffc90003c66cd8 EFLAGS: 00010002
RAX: 1ffffd1ffffa86eb RBX: 0000000000000000 RCX: 0000000000080000
RDX: ffffc90006012000 RSI: 0000000000000f55 RDI: 0000000000000f56
RBP: 0000000000000040 R08: 0000000000000000 R09: 0000000000000000
R10: dffffc0000000000 R11: fffff5200078cda4 R12: 0000000000000010
R13: dffffc0000000000 R14: 0000000000000002 R15: 0000000000000010
FS: 00007f19792606c0(0000) GS:ffff8881234d2000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f1978392640 CR3: 0000000076f1c000 CR4: 00000000003526f0
Call Trace:
<TASK>
trace_note+0x1b0/0x200 kernel/trace/blktrace.c:205
trace_note_tsk+0xa0/0x130 kernel/trace/blktrace.c:221
__blk_add_trace+0x82b/0x9f0 kernel/trace/blktrace.c:454
blk_add_trace_bio+0x2ab/0x2f0 kernel/trace/blktrace.c:1113
__do_trace_block_bio_queue include/trace/events/block.h:450 [inline]
trace_block_bio_queue include/trace/events/block.h:450 [inline]
submit_bio_noacct_nocheck+0xaa1/0xb10 block/blk-core.c:771
bh_submit fs/buffer.c:1163 [inline]
__bh_read+0x7d/0x180 fs/buffer.c:2957
bh_read include/linux/buffer_head.h:512 [inline]
ocfs2_get_sector+0x360/0x480 fs/ocfs2/super.c:1721
ocfs2_sb_probe fs/ocfs2/super.c:752 [inline]
ocfs2_fill_super+0x3e7/0x6a40 fs/ocfs2/super.c:988
get_tree_bdev_flags+0x436/0x500 fs/super.c:2104
vfs_get_tree+0x92/0x2a0 fs/super.c:2164
fc_mount fs/namespace.c:1300 [inline]
do_new_mount_fc fs/namespace.c:4030 [inline]
do_new_mount+0x325/0xb00 fs/namespace.c:4106
do_mount fs/namespace.c:4470 [inline]
__do_sys_mount fs/namespace.c:4683 [inline]
__se_sys_mount+0x31d/0x420 fs/namespace.c:4660
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x171/0x540 arch/x86/entry/syscall_64.c:89
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x00007f19783aa77a
Code: 48 c7 c2 e8 ff ff ff f7 d8 64 89 02 b8 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f197925fe58 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5
RAX: ffffffffffffffda RBX: 00007f197925fee0 RCX: 00007f19783aa77a
RDX: 0000200000000380 RSI: 00002000000003c0 RDI: 00007f197925fea0
RBP: 0000200000000380 R08: 00007f197925fee0 R09: 0000000000200000
R10: 0000000000200000 R11: 0000000000000246 R12: 00002000000003c0
R13: 00007f197925fea0 R14: 0000000000004703 R15: 0000200000000500
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:relay_reserve include/linux/relay.h:258 [inline]
RIP: 0010:relay_blktrace_event1 kernel/trace/blktrace.c:124 [inline]
RIP: 0010:relay_blktrace_event+0x34f/0x6e0 kernel/trace/blktrace.c:165
Code: d8 48 c1 e8 03 42 80 3c 28 00 74 08 48 89 df e8 e7 c6 76 00 4b 8d 2c 27 48 83 c5 30 48 8b 1b 4c 8d 63 10 4d 89 e6 49 c1 ee 03 <43> 80 3c 2e 00 74 08 4c 89 e7 e8 c2 c6 76 00 4c 8b 7b 10 49 01 ef
RSP: 0018:ffffc90003c66cd8 EFLAGS: 00010002
RAX: 1ffffd1ffffa86eb RBX: 0000000000000000 RCX: 0000000000080000
RDX: ffffc90006012000 RSI: 0000000000000f55 RDI: 0000000000000f56
RBP: 0000000000000040 R08: 0000000000000000 R09: 0000000000000000
R10: dffffc0000000000 R11: fffff5200078cda4 R12: 0000000000000010
R13: dffffc0000000000 R14: 0000000000000002 R15: 0000000000000010
FS: 00007f19792606c0(0000) GS:ffff8881234d2000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f1978392640 CR3: 0000000076f1c000 CR4: 00000000003526f0
----------------
Code disassembly (best guess):
0: d8 48 c1 fmuls -0x3f(%rax)
3: e8 03 42 80 3c call 0x3c80420b
8: 28 00 sub %al,(%rax)
a: 74 08 je 0x14
c: 48 89 df mov %rbx,%rdi
f: e8 e7 c6 76 00 call 0x76c6fb
14: 4b 8d 2c 27 lea (%r15,%r12,1),%rbp
18: 48 83 c5 30 add $0x30,%rbp
1c: 48 8b 1b mov (%rbx),%rbx
1f: 4c 8d 63 10 lea 0x10(%rbx),%r12
23: 4d 89 e6 mov %r12,%r14
26: 49 c1 ee 03 shr $0x3,%r14
* 2a: 43 80 3c 2e 00 cmpb $0x0,(%r14,%r13,1) <-- trapping instruction
2f: 74 08 je 0x39
31: 4c 89 e7 mov %r12,%rdi
34: e8 c2 c6 76 00 call 0x76c6fb
39: 4c 8b 7b 10 mov 0x10(%rbx),%r15
3d: 49 01 ef add %rbp,%r15
* Struck through repros no longer work on HEAD.