BUG: unable to handle kernel NULL pointer dereference in __rcu_free_sheaf_prepare

Status: moderation: reported on 2026/11/01 20:38
Subsystems: virt
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+b901c4ba5348c9a056df@syzkaller.appspotmail.com
First crash: 4d14h, last: 4d14h
✨ AI-generated root cause summary:

In the SLUB sheaf management subsystem, __rcu_free_sheaf_prepare() processes objects in an RCU sheaf after the grace period without validating the slab returned by virt_to_slab(p[i]). If the underlying slab page has been freed or its PGTY_slab page type cleared, virt_to_slab() returns NULL. Passing this NULL slab pointer to memcg_slab_free_hook() triggers an unconditional read of slab->obj_exts at offset 0x38, causing an immediate kernel NULL pointer dereference and system crash in softirq/RCU context.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 65ba8f34ec88:

Root Cause and Crash Analysis

1. Crash Site and Exact Failure Mechanism

The crash occurs in softirq / RCU callback processing:

rcu_core() -> rcu_do_batch() -> rcu_free_sheaf() -> __rcu_free_sheaf_prepare()

The trapping instruction is:

mov 0x38(%rbx), %r8

where %rbx == 0x0 (NULL), attempting to access offset 0x38 (56 bytes).

In mm/slub.c:3036-3038, __rcu_free_sheaf_prepare() processes each object queued in the RCU sheaf:

static bool __rcu_free_sheaf_prepare(struct kmem_cache *s,
				     struct slab_sheaf *sheaf)
{
	...
	while (i < sheaf->size) {
		struct slab *slab = virt_to_slab(p[i]);

		memcg_slab_free_hook(s, slab, p + i, 1);
		...

memcg_slab_free_hook() in mm/slub.c:2572 unconditionally calls slab_obj_exts(slab):

static inline unsigned long slab_obj_exts(struct slab *slab)
{
	unsigned long obj_exts = READ_ONCE(slab->obj_exts); // slab->obj_exts is at offset 0x38 (56 bytes)
	return obj_exts;
}

Because slab == NULL, accessing slab->obj_exts (0x38(%rbx)) results in a supervisor read page fault at 0x0000000000000038, causing an immediate kernel NULL pointer dereference panic.

2. Disassembly & Page Invariant Verification

In virt_to_slab(addr):

static inline struct slab *page_slab(const struct page *page)
{
	page = compound_head(page);
	if (data_race(page->page_type >> 24) != PGTY_slab)
		page = NULL;
	return (struct slab *)page;
}

The disassembled instructions leading up to the crash verify that page_slab() executed:

cmpb   $0xf5, 0x33(%rbx)   ; PGTY_slab == 0xf5 at offset 0x33 of struct page
cmovne %rax, %rbx          ; %rax is 0, so %rbx becomes NULL if page_type != PGTY_slab
mov    0x38(%rbx), %r8     ; Dereference 0x38(NULL) -> CRASH

This confirms that when the RCU grace period expired and rcu_free_sheaf() ran, the page containing object p[0] no longer had PGTY_slab set (i.e., it was not a slab page or had already been returned to the buddy allocator via free_slab() -> __free_slab()).

3. Object Lifecycle and Hypotheses Evaluated

  • KFENCE Pool Objects (Ruled Out): kfence_init_pool() in mm/kfence/core.c unconditionally executes __SetPageSlab(page) on all pool pages at boot, and their page_type is never cleared. Therefore, KFENCE objects do not produce virt_to_slab() == NULL.
  • Bootstrap Sheaves (Ruled Out): Bootstrap sheaves have capacity 0 and are never handed to RCU sheaf freeing paths (__kfree_rcu_sheaf() checks cache_has_sheaves(s)).
  • Missing NULL Validation in __rcu_free_sheaf_prepare (Verified): virt_to_slab() is known to be able to return NULL for addresses whose slab pages have been reclaimed or corrupted, but __rcu_free_sheaf_prepare() performs no check for !slab before passing it to memcg_slab_free_hook(s, slab, ...).
  • Trigger Scenario (Partially Inconclusive): An object can be queued into an RCU sheaf via kfree_call_rcu_nolock() or kfree_rcu_sheaf(). At the moment of queuing, virt_to_slab() is verified to be non-NULL. However, during the grace period before rcu_free_sheaf() executes:
    • If a caller double-frees an object (e.g. through a race between kfree()/kmem_cache_free() and kfree_rcu()), the slab's inuse count reaches 0 from the regular free path, allowing discard_slab()/free_slab() to return the slab page to the buddy allocator and clear PGTY_slab.
    • Alternatively, if cache shrinking or tearing down occurs, or in interaction with CONFIG_SLUB_RCU_DEBUG (which defers object reuse for SLAB_TYPESAFE_BY_RCU), a slab page may be freed before the sheaf's RCU callback processes its cached object pointer. Pinpointing the exact offending caller/driver that queued the specific object p[0] is inconclusive from the report alone because syzkaller's report captures only the softirq RCU callback execution on the idle CPU (swapper/2), not the earlier allocation or freeing stack trace.

Classification & Direct Consequences

  • RootCauseFiles: mm/slub.c, mm/slab.h
  • MemoryCorruption: false The direct crash is a pure NULL-pointer dereference at 0x0000000000000038 when reading slab->obj_exts. Pure NULL-pointer dereferences do not corrupt memory.
  • InfoLeak: "none" No kernel memory or pointer values are disclosed to userspace or an observer.
  • RawDenialOfService: true The supervisor read access to address 0x38 triggers a kernel Oops/panic in interrupt/softirq context (rcu_core), halting the machine.
  • IsNetCandidate: false The bug is in core SLUB memory management and RCU callback execution; no networking subsystems or network packets are involved in the crash trace.
  • IsKVMCandidate: false No KVM or virtualization components are involved.
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
00219754 repro-c 💥 BUG: unable to handle kernel NULL pointer dereference in __rcu_free_sheaf_prepare 2026/10/31 06:13 2026/10/31 06:13 2026/10/31 14:21 2041d3f8
516ccae9 assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ BUG: unable to handle kernel NULL pointer dereference in __rcu_free_sheaf_prepare 2026/10/28 20:34 2026/10/28 20:34 2026/10/28 21:36 b1d00f76

Sample crash report:
BUG: kernel NULL pointer dereference, address: 0000000000000038
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 2c141067 P4D 2c141067 PUD 0 
Oops: Oops: 0000 [#1] SMP KASAN NOPTI
CPU: 2 UID: 0 PID: 0 Comm: swapper/2 Tainted: G             L       syzkaller #0 PREEMPT(full) 
Tainted: [L]=SOFTLOCKUP
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:slab_obj_exts mm/slab.h:701 [inline]
RIP: 0010:memcg_slab_free_hook mm/slub.c:2572 [inline]
RIP: 0010:__rcu_free_sheaf_prepare+0xc4/0x300 mm/slub.c:3038
Code: c1 e0 06 48 03 05 7c 02 13 0c 48 8b 58 08 48 89 da 83 e2 01 48 83 ea 01 48 09 d3 48 21 c3 31 c0 80 7b 33 f5 48 0f 45 d8 66 90 <4c> 8b 43 38 4d 85 c0 0f 85 37 01 00 00 49 8b 2c 24 41 8b 77 1c 48
RSP: 0018:ffffc90000648d80 EFLAGS: 00010206
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 000000000000002e
RDX: ffffffffffffffff RSI: ffffffff8e6212d7 RDI: ffff888060a4fd00
RBP: ffff888060a11e80 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 000000000000ad58 R12: ffff88804be6bc20
R13: 0000000000000000 R14: ffff88804be6bc00 R15: ffff88801d4428c0
FS:  0000000000000000(0000) GS:ffff8880d4690000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000038 CR3: 0000000051b51000 CR4: 0000000000352ef0
DR0: ffffffffffffffff DR1: 00000000000001f8 DR2: 0000000000000002
DR3: ffffffffefffff15 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Call Trace:
 <IRQ>
 rcu_free_sheaf+0x1a/0xe0 mm/slub.c:6182
 rcu_do_batch kernel/rcu/tree.c:2650 [inline]
 rcu_core+0x5f0/0x1220 kernel/rcu/tree.c:2919
 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:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:64
Code: 77 a3 02 e9 43 61 03 00 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d c3 0d 20 00 fb f4 <c3> cc cc cc cc 66 2e 0f 1f 84 00 00 00 00 00 66 90 90 90 90 90 90
RSP: 0018:ffffc90000187e00 EFLAGS: 00000202
RAX: 00000000000ff1a7 RBX: ffff888020ac4b00 RCX: ffffffff8bebb325
RDX: 0000000000000001 RSI: ffffffff8e6212d7 RDI: ffffffff8c833580
RBP: ffffed1004158960 R08: 0000000000000001 R09: ffffed100d446825
R10: ffff88806a23412b R11: 0000000000000001 R12: 0000000000000000
R13: 0000000000000000 R14: 1ffff92000030fc4 R15: dffffc0000000000
 arch_safe_halt arch/x86/include/asm/paravirt.h:62 [inline]
 default_idle+0x9/0x10 arch/x86/kernel/process.c:768
 default_idle_call+0x6c/0xb0 kernel/sched/idle.c:122
 cpuidle_idle_call kernel/sched/idle.c:199 [inline]
 do_idle+0x3ae/0x5b0 kernel/sched/idle.c:355
 cpu_startup_entry+0x4f/0x60 kernel/sched/idle.c:454
 start_secondary+0x21d/0x2d0 arch/x86/kernel/smpboot.c:312
 common_startup_64+0x13e/0x158
 </TASK>
Modules linked in:
CR2: 0000000000000038
---[ end trace 0000000000000000 ]---
RIP: 0010:slab_obj_exts mm/slab.h:701 [inline]
RIP: 0010:memcg_slab_free_hook mm/slub.c:2572 [inline]
RIP: 0010:__rcu_free_sheaf_prepare+0xc4/0x300 mm/slub.c:3038
Code: c1 e0 06 48 03 05 7c 02 13 0c 48 8b 58 08 48 89 da 83 e2 01 48 83 ea 01 48 09 d3 48 21 c3 31 c0 80 7b 33 f5 48 0f 45 d8 66 90 <4c> 8b 43 38 4d 85 c0 0f 85 37 01 00 00 49 8b 2c 24 41 8b 77 1c 48
RSP: 0018:ffffc90000648d80 EFLAGS: 00010206
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 000000000000002e
RDX: ffffffffffffffff RSI: ffffffff8e6212d7 RDI: ffff888060a4fd00
RBP: ffff888060a11e80 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 000000000000ad58 R12: ffff88804be6bc20
R13: 0000000000000000 R14: ffff88804be6bc00 R15: ffff88801d4428c0
FS:  0000000000000000(0000) GS:ffff8880d4690000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000038 CR3: 0000000051b51000 CR4: 0000000000352ef0
DR0: ffffffffffffffff DR1: 00000000000001f8 DR2: 0000000000000002
DR3: ffffffffefffff15 DR6: 00000000ffff0ff0 DR7: 0000000000000400
----------------
Code disassembly (best guess):
   0:	c1 e0 06             	shl    $0x6,%eax
   3:	48 03 05 7c 02 13 0c 	add    0xc13027c(%rip),%rax        # 0xc130286
   a:	48 8b 58 08          	mov    0x8(%rax),%rbx
   e:	48 89 da             	mov    %rbx,%rdx
  11:	83 e2 01             	and    $0x1,%edx
  14:	48 83 ea 01          	sub    $0x1,%rdx
  18:	48 09 d3             	or     %rdx,%rbx
  1b:	48 21 c3             	and    %rax,%rbx
  1e:	31 c0                	xor    %eax,%eax
  20:	80 7b 33 f5          	cmpb   $0xf5,0x33(%rbx)
  24:	48 0f 45 d8          	cmovne %rax,%rbx
  28:	66 90                	xchg   %ax,%ax
* 2a:	4c 8b 43 38          	mov    0x38(%rbx),%r8 <-- trapping instruction
  2e:	4d 85 c0             	test   %r8,%r8
  31:	0f 85 37 01 00 00    	jne    0x16e
  37:	49 8b 2c 24          	mov    (%r12),%rbp
  3b:	41 8b 77 1c          	mov    0x1c(%r15),%esi
  3f:	48                   	rex.W

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/28 20:34 upstream 65ba8f34ec88 858db2ec .config console log report info [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu-upstream BUG: unable to handle kernel NULL pointer dereference in __rcu_free_sheaf_prepare
* Struck through repros no longer work on HEAD.