BUG: unable to handle kernel NULL pointer dereference in __refill_objects_node

Status: moderation: reported on 2026/11/01 02:26
Subsystems: bluetooth
Labels: prio:high
[Documentation on labels]
Reported-by: syzbot+35c18d9d31d4326e6b81@syzkaller.appspotmail.com
First crash: 4d02h, last: 4d02h
✨ AI-generated root cause summary:

In mm/slub.c, __refill_objects_node() replenishes percpu sheaves by detaching a freelist from a partial slab via get_freelist_nofreeze() and iterating through the freelist pointers in a while (count && refilled < max) loop. Because the loop condition only checks count and refilled rather than validating that object is non-NULL, a prematurely terminated or corrupted freelist (where fewer objects are linked in slab->freelist than the expected count = slab->objects - slab->inuse) causes object to become NULL before count reaches zero. On the subsequent iteration, get_freepointer() dereferences NULL + s->offset (offset 0x400 for kmalloc-2k), resulting in an immediate kernel NULL pointer dereference crash at address 0x0000000000000400.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 7a19f0cc94ad:

Technical Root Cause Analysis

1. Functions, Structs, and Files Involved

  • Source File: mm/slub.c
  • Functions:
  • Caller Context: l2cap_chan_create() in net/bluetooth/l2cap_core.c:447 allocating a struct l2cap_chan via kzalloc_obj() from the kmalloc-2k slab cache (s->offset = 0x400 / 1024, s->size = 2048).
  • Structs and Fields:
    • struct kmem_cache: s->offset (freepointer byte offset, 0x400 / 1024 for kmalloc-2k).
    • struct slab: slab->freelist and slab->counters (inuse, objects, frozen).
    • struct freelist_counters: counters union holding inuse:16, objects:15, and frozen:1.
    • struct slub_percpu_sheaves: per-CPU sheaf cache structure managing batch allocations.

2. Failure Mechanism and Invariant Violation

  1. Trigger Path: A userspace process issues a socket(AF_BLUETOOTH, ...) syscall. In l2cap_chan_create(), the kernel calls kzalloc_obj(*chan, GFP_ATOMIC) to allocate struct l2cap_chan. This invokes alloc_from_pcs() in the SLUB allocator to fetch an object from the local per-CPU sheaf (PCS). Because the sheaf is empty, alloc_from_pcs() calls __pcs_replace_empty_main() $\to$ refill_sheaf() $\to$ refill_objects() $\to$ __refill_objects_node().

  2. Detaching Freelist via get_freelist_nofreeze(): __refill_objects_node() calls get_partial_node_bulk() to grab partial slabs from n->partial into a local list pc.slabs. For the slab at 0xfffffdffc322ee00, get_freelist_nofreeze() is called:

    static inline void *get_freelist_nofreeze(struct kmem_cache *s, struct slab *slab,
                                              unsigned int *count)
    {
        ...
        do {
            old.freelist = slab->freelist;
            old.counters = slab->counters;
    
            new.freelist = NULL;
            new.counters = old.counters;
            new.inuse = old.objects;
        } while (!slab_update_freelist(s, slab, &old, &new, "get_freelist_nofreeze"));
    
        *count = old.objects - old.inuse;
        return old.freelist;
    }
    

    At the crash site, old.counters was 0x00080002 (objects = 8, inuse = 2), and new.counters was 0x00080008 (objects = 8, inuse = 8). Based on the counters, get_freelist_nofreeze() computed *count = old.objects - old.inuse = 6.

  3. Loop Invariant Violation in __refill_objects_node(): __refill_objects_node() iterates over the detached freelist:

    object = get_freelist_nofreeze(s, slab, &count);
    
    while (count && refilled < max) {
        p[refilled] = object;
        object = get_freepointer(s, object);
        maybe_wipe_obj_freeptr(s, p[refilled]);
    
        refilled++;
        count--;
    }
    

    The loop invariant assumes that the singly-linked object freelist chain contains at least count valid objects. However:

    • On this slab, after popping 3 objects (refilled = 0, 1, 2), the third object contained 0x0 (NULL) at offset 0x400, causing object to become NULL prematurely, while 3 free objects were still accounted for by count.
    • The loop condition while (count && refilled < max) only verifies count != 0 and refilled < max, without verifying object != NULL.
    • On the 4th iteration (refilled = 3, max = 4, count > 0), p[3] = object writes NULL into the sheaf array, and the code immediately invokes:
      object = get_freepointer(s, object);
      
    • In get_freepointer():
      ptr_addr = (unsigned long)object + s->offset; // 0 + 0x400 = 0x400
      p = *(freeptr_t *)(ptr_addr);                 // ldr x28, [x8, x27] where x27=0, x8=0x400
      
    • The CPU dereferences address 0x0000000000000400, triggering a kernel translation fault / Data Abort (ESR = 0x96000005, FSC = 0x05: level 1 translation fault).

3. Direct Consequences in Production

  • Memory Corruption (MemoryCorruption: true): The defect stems from slab freelist / counter corruption: the freelist pointer chain terminated prematurely while the accounting metadata (slab->objects - slab->inuse) indicated objects were still free. Furthermore, if execution did not fault at get_freepointer(), storing NULL into p[refilled] would propagate a NULL pointer into the per-CPU sheaf as a valid allocated object. A subsequent allocator caller retrieving this slot receives a NULL pointer that is treated as an active heap object, causing severe data structure inconsistency and memory corruption.
  • Denial of Service (RawDenialOfService: true): The invalid memory access occurs in kernel mode during syscall processing (sys_socket), resulting in an immediate kernel oops/panic. In production kernels, this fatal data abort halts execution or crashes the active task holding critical locks.
  • Information Leak (InfoLeak: "none"): Execution halts abruptly on the invalid load at 0x400. No kernel memory contents are exposed or returned to userspace.
  • Network Candidate (IsNetCandidate: true): The crash occurs during network socket creation for Bluetooth L2CAP (bt_sock_create() / l2cap_sock_create() / l2cap_chan_create()).
  • KVM Candidate (IsKVMCandidate: false): The issue is purely within core kernel slab memory management and the Bluetooth socket subsystem, with no relation to virtualization or KVM.
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
c8a8ff13 repro-c 💥 BUG: unable to handle kernel NULL pointer dereference in __refill_objects_node 2026/10/31 06:10 2026/10/31 06:10 2026/10/31 15:57 2041d3f8
a0c5a771 assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ✅ UserNamespace: ✅ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ BUG: unable to handle kernel NULL pointer dereference in __refill_objects_node 2026/10/29 03:45 2026/10/29 03:45 2026/10/29 04:09 b1d00f76

Sample crash report:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000400
Mem abort info:
  ESR = 0x0000000096000005
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
  FSC = 0x05: level 1 translation fault
Data abort info:
  ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
  CM = 0, WnR = 0, TnD = 0, TagAccess = 0
  GCS = 0, Overlay = 0, DirtyBit = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000130a12000
[0000000000000400] pgd=0800000141542403, p4d=0800000141542403, pud=0000000000000000
Internal error: Oops: 0000000096000005 [#1]  SMP
Modules linked in:
CPU: 1 UID: 0 PID: 8392 Comm: syz.0.758 Tainted: G   M                syzkaller #0 PREEMPT 
Tainted: [M]=MACHINE_CHECK
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/14/2026
pstate: 83400005 (Nzcv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : get_freepointer mm/slub.c:564 [inline]
pc : __refill_objects_node+0x308/0x510 mm/slub.c:7292
lr : get_freelist_nofreeze mm/slub.c:4457 [inline]
lr : __refill_objects_node+0x2b8/0x510 mm/slub.c:7288
sp : ffff8000963378d0
x29: ffff800096337960 x28: 0000000000000000 x27: 0000000000000000
x26: 0000000000000004 x25: fffffdffc322ee00 x24: 0000000000000003
x23: 0000000000000004 x22: 0000000000000004 x21: 0000000000000001
x20: ffff0000c1b2f938 x19: 0000000000000004 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000001
x14: 0000000000886339 x13: 0000000000000001 x12: 0000000000000003
x11: 0000000000000000 x10: fffffdffc322ee20 x9 : ffff0000c1b2f920
x8 : 0000000000000400 x7 : 0000000000000000 x6 : 0000000000000000
x5 : 0000000000000001 x4 : 0000000000000008 x3 : 0000000000080008
x2 : 0000000000000000 x1 : 0000000000080002 x0 : 0000000000000001
Call trace:
 get_freepointer mm/slub.c:563 [inline] (P)
 __refill_objects_node+0x308/0x510 mm/slub.c:7292 (P)
 refill_objects+0x78/0x320 mm/slub.c:7405
 refill_sheaf+0x44/0x6c mm/slub.c:2885
 __pcs_replace_empty_main+0x22c/0x4f4 mm/slub.c:4774
 alloc_from_pcs mm/slub.c:4850 [inline]
 slab_alloc_node mm/slub.c:4984 [inline]
 __kmalloc_cache_noprof+0x37c/0x5fc mm/slub.c:5559
 _kmalloc_noprof include/linux/slab.h:991 [inline]
 _kzalloc_noprof include/linux/slab.h:1312 [inline]
 l2cap_chan_create+0x58/0x6ec net/bluetooth/l2cap_core.c:447
 l2cap_sock_create+0x128/0x2d0 net/bluetooth/l2cap_sock.c:2063
 bt_sock_create+0x14c/0x240 net/bluetooth/af_bluetooth.c:132
 __sock_create+0x31c/0x670 net/socket.c:1676
 sock_create net/socket.c:1734 [inline]
 __sys_socket_create+0xc0/0x11c net/socket.c:1771
 __sys_socket+0x44/0xec net/socket.c:1818
 __do_sys_socket net/socket.c:1832 [inline]
 __se_sys_socket net/socket.c:1830 [inline]
 __arm64_sys_socket+0x84/0x9c net/socket.c:1830
 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
 invoke_syscall+0x98/0x244 arch/arm64/kernel/syscall.c:49
 el0_svc_common+0xec/0x23c arch/arm64/kernel/syscall.c:121
 do_el0_svc+0x4c/0x5c arch/arm64/kernel/syscall.c:140
 el0_svc+0x60/0x1d8 arch/arm64/kernel/entry-common.c:796
 el0t_64_sync_handler+0x44/0x104 arch/arm64/kernel/entry-common.c:815
 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:606
Code: f9401fe8 f900029b 2a1a03f7 b9402908 (f87b691c) 
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
   0:	f9401fe8 	ldr	x8, [sp, #56]
   4:	f900029b 	str	x27, [x20]
   8:	2a1a03f7 	mov	w23, w26
   c:	b9402908 	ldr	w8, [x8, #40]
* 10:	f87b691c 	ldr	x28, [x8, x27] <-- trapping instruction

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/10/28 02:17 git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci 7a19f0cc94ad 96923c60 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-gce-arm64 BUG: unable to handle kernel NULL pointer dereference in __refill_objects_node
* Struck through repros no longer work on HEAD.