syzbot
BUG: spinlock bad magic in sock_wfree
Status: closed as invalid on 2026/11/08 10:18
Subsystems:
net
Labels:
prio:normal
[Documentation on labels]
First crash: 3d11h, last: 3d11h
✨ AI-generated root cause summary:
Unrestricted writes to PCI configuration space via /proc/bus/pci/ (proc_bus_pci_write() and pci_user_write_config_dword()) allow userspace to overwrite critical host bridge registers. On a QEMU Q35 system, writing to device 00:00.0 at offset 0x60 modifies the PCIEXBAR register and sets bit 0 (PCIEXBAREN), remapping the PCIe MMCONFIG aperture directly over active system physical RAM. Subsequent reads from kernel data structures located in the shadowed physical address range (such as socket waitqueues at physical address 0x46117940) return all-ones (0xffffffff), causing waitqueue checks to spuriously succeed and triggering a 'spinlock bad magic' bug (.magic: ffffffff) when attempting to acquire the lock.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Involved Functions, Structs, and Files
2. Trigger Mechanism and Sequence of Events
-
User-space PCI Configuration Write:
The reproducer mounts procfs and opens ./proc/bus/pci/00/00.0, corresponding to the PCI Host Bridge (Intel Q35 MCH). It performs repeated writes of a 125-byte payload into configuration space.
-
Overwriting PCIEXBAR on the Host Bridge:
In proc_bus_pci_write(), the write loop invokes pci_user_write_config_dword() for each 4-byte chunk. At offset pos = 0x60 (as recorded in the task frame: RBP = 0x60), the user writes value 0x4ce5e1cb (R13 = 0x4ce5e1cb, matching bytes cb e1 e5 4c from offset 0x60 in the reproducer payload).
- On the QEMU Q35 MCH, register
0x60 is PCIEXBAR (PCI Express Register Range Base Address / MMCONFIG ECAM window).
- Bit 0 of
0x4ce5e1cb is 1, which asserts PCIEXBAREN (enable PCIe ECAM).
- This reconfigures the host bridge to map the PCIe MMCONFIG MMIO aperture over physical memory in the
0x40000000..0x47ffffff range, superseding the physical RAM mapping for those addresses.
-
Interrupt Handling and Direct Map Access:
Inside pci_user_write_config_dword(), right after writing to hardware, raw_spin_unlock_irq(&pci_lock) is executed. As interrupts are re-enabled (sti), a pending APIC timer interrupt immediately vectors to sysvec_apic_timer_interrupt.
- In timer softirq context,
addrconf_rs_timer() invokes ndisc_send_rs() -> ndisc_send_skb().
- The packet traverses IPv6 output and is handed off to an XFRM interface device (
xfrmi_xmit()), where transmission error handling drops the skb with kfree_skb(skb).
kfree_skb() invokes skb_release_head_state(), calling the socket destructor sock_wfree() on skb->sk (net->ipv6.ndisc_sk).
sock_wfree() calls sock_def_write_space_wfree(sk, ...).
-
Lock Read Corruption and spin_bug:
sock_def_write_space_wfree() dereferences wq = rcu_dereference(sk->sk_wq) and tests waitqueue_active(&wq->wait).
- The socket waitqueue structure
wq->wait resides at virtual address 0xffff888046117940 in the kernel direct-map (PAGE_OFFSET), corresponding to physical address 0x46117940.
- Because physical address
0x46117940 was just placed under the newly enabled PCIe MMCONFIG aperture, CPU reads targeting this physical range bypass system RAM and hit unpopulated PCIe configuration space, which floats high and returns all-ones (0xffffffff).
list_empty(&wq->wait.head) reads head->next as 0xffffffffffffffff, spuriously evaluating waitqueue_active() to true.
wake_up_interruptible_sync_poll() then invokes __wake_up_sync_key() -> __wake_up_common_lock(), attempting to acquire spin_lock_irqsave(&wq->wait.lock, flags).
- In
debug_spin_lock_before(), the lock fields read from 0xffff888046117940 are all-ones:
lock->magic is 0xffffffff (expected SPINLOCK_MAGIC = 0xdead4ead)
lock->owner is 0xffffffffffffffff (<none>/-1)
lock->owner_cpu is 0xffffffff (-1)
- This triggers the
spin_bug("bad magic") splat.
Sample crash report:
BUG: spinlock bad magic on CPU#1,
BUG: spinlock bad magic on CPU#1, syz.3.20/6131
lock: 0xffff888046117940, .magic: ffffffff, .owner: <none>/-1, .owner_cpu: -1
CPU: 1 UID: 0 PID: 6131 Comm: syz.3.20 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
spin_bug kernel/locking/spinlock_debug.c:78 [inline]
debug_spin_lock_before kernel/locking/spinlock_debug.c:86 [inline]
do_raw_spin_lock.cold+0x37/0x3c kernel/locking/spinlock_debug.c:115
__raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:156 [inline]
_raw_spin_lock_irqsave+0x42/0x60 kernel/locking/spinlock.c:181
__wake_up_common_lock kernel/sched/wait.c:124 [inline]
__wake_up_sync_key+0x1c/0x50 kernel/sched/wait.c:188
sock_def_write_space_wfree net/core/sock.c:3780 [inline]
sock_wfree+0x5c9/0x7f0 net/core/sock.c:2734
skb_release_head_state+0x35f/0x400 net/core/skbuff.c:1166
skb_release_all net/core/skbuff.c:1186 [inline]
__kfree_skb net/core/skbuff.c:1202 [inline]
sk_skb_reason_drop+0xc4/0x1b0 net/core/skbuff.c:1241
kfree_skb_reason include/linux/skbuff.h:1329 [inline]
kfree_skb include/linux/skbuff.h:1338 [inline]
xfrmi_xmit+0xd5c/0x23f0 net/xfrm/xfrm_interface_core.c:589
__netdev_start_xmit include/linux/netdevice.h:5457 [inline]
netdev_start_xmit include/linux/netdevice.h:5466 [inline]
xmit_one net/core/dev.c:4068 [inline]
dev_hard_start_xmit+0x121/0x760 net/core/dev.c:4084
__dev_queue_xmit+0x1bef/0x48d0 net/core/dev.c:5088
dev_queue_xmit include/linux/netdevice.h:3485 [inline]
neigh_connected_output+0x3b5/0x5d0 net/core/neighbour.c:1662
neigh_output include/net/neighbour.h:554 [inline]
ip6_finish_output2+0xbd8/0x1f10 net/ipv6/ip6_output.c:138
__ip6_finish_output+0x34e/0xdf0 net/ipv6/ip6_output.c:210
ip6_finish_output net/ipv6/ip6_output.c:221 [inline]
NF_HOOK_COND include/linux/netfilter.h:314 [inline]
ip6_output+0x2a1/0xa50 net/ipv6/ip6_output.c:248
dst_output include/net/dst.h:471 [inline]
NF_HOOK include/linux/netfilter.h:325 [inline]
ndisc_send_skb+0xa8e/0x1bf0 net/ipv6/ndisc.c:513
ndisc_send_rs+0x129/0x680 net/ipv6/ndisc.c:724
addrconf_rs_timer+0x424/0x880 net/ipv6/addrconf.c:4099
call_timer_fn+0x198/0x5f0 kernel/time/timer.c:1749
expire_timers kernel/time/timer.c:1800 [inline]
__run_timers+0x757/0xb00 kernel/time/timer.c:2375
__run_timer_base kernel/time/timer.c:2387 [inline]
__run_timer_base kernel/time/timer.c:2379 [inline]
run_timer_base+0x114/0x190 kernel/time/timer.c:2396
run_timer_softirq+0x1a/0x50 kernel/time/timer.c:2406
handle_softirqs+0x216/0xa20 kernel/softirq.c:669
__do_softirq kernel/softirq.c:703 [inline]
invoke_softirq kernel/softirq.c:542 [inline]
__irq_exit_rcu+0x178/0x220 kernel/softirq.c:786
irq_exit_rcu+0x9/0x30 kernel/softirq.c:809
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:__raw_spin_unlock_irq include/linux/spinlock_api_smp.h:220 [inline]
RIP: 0010:_raw_spin_unlock_irq+0x29/0x50 kernel/locking/spinlock.c:229
Code: 90 f3 0f 1e fa 53 48 8b 74 24 08 48 89 fb 48 83 c7 18 e8 8a 1b f7 f5 48 89 df e8 12 72 f7 f5 e8 8d 86 24 f6 fb bf 01 00 00 00 <e8> c2 3a e6 f5 65 48 83 3d 51 64 c8 09 00 74 06 5b e9 01 4d 00 00
RSP: 0018:ffffc90003487c28 EFLAGS: 00000206
RAX: 0000000000000517 RBX: ffffffff90cc2ac0 RCX: 0000000000000080
RDX: 0000000000000001 RSI: ffffffff8e629989 RDI: 0000000000000001
RBP: 0000000000000060 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff8880247aa000
R13: 000000004ce5e1cb R14: ffffffff8be39d00 R15: 0000000000000019
pci_user_write_config_dword+0x150/0x230 drivers/pci/access.c:278
proc_bus_pci_write+0x531/0x5e0 drivers/pci/proc.c:173
pde_write fs/proc/inode.c:330 [inline]
proc_reg_write+0x240/0x330 fs/proc/inode.c:342
vfs_write+0x2aa/0x1050 fs/read_write.c:685
ksys_write+0x12a/0x250 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f1f2d39fb19
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 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:00007f1f2e2a6028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f1f2d625fa0 RCX: 00007f1f2d39fb19
RDX: 000000000000007d RSI: 0000200000000240 RDI: 0000000000000003
RBP: 00007f1f2d437b70 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f1f2d626038 R14: 00007f1f2d625fa0 R15: 00007ffeebd3c9f8
</TASK>
----------------
Code disassembly (best guess):
0: 90 nop
1: f3 0f 1e fa endbr64
5: 53 push %rbx
6: 48 8b 74 24 08 mov 0x8(%rsp),%rsi
b: 48 89 fb mov %rdi,%rbx
e: 48 83 c7 18 add $0x18,%rdi
12: e8 8a 1b f7 f5 call 0xf5f71ba1
17: 48 89 df mov %rbx,%rdi
1a: e8 12 72 f7 f5 call 0xf5f77231
1f: e8 8d 86 24 f6 call 0xf62486b1
24: fb sti
25: bf 01 00 00 00 mov $0x1,%edi
* 2a: e8 c2 3a e6 f5 call 0xf5e63af1 <-- trapping instruction
2f: 65 48 83 3d 51 64 c8 cmpq $0x0,%gs:0x9c86451(%rip) # 0x9c86489
36: 09 00
38: 74 06 je 0x40
3a: 5b pop %rbx
3b: e9 01 4d 00 00 jmp 0x4d41
* Struck through repros no longer work on HEAD.