Re: [PATCH for-next v3 7/9] mm/slab: introduce kfree_rcu_nolock()

From: Harry Yoo

Date: Mon Jun 22 2026 - 01:30:04 EST




On 6/21/26 9:29 AM, XIAO WU wrote:
> Hi,

Hi Xiao,

> I noticed the Sashiko AI review [1] in this thread flagged that
> kfree_call_rcu_nolock() dereferences slab->slab_cache even when
> virt_to_slab() returns NULL (for large kmalloc objects that bypass
> SLUB, or vmalloc addresses). The VM_WARN_ON_ONCE fires but does not
> stop execution, and the subsequent NULL dereference is deterministic.

Thanks for taking a look, but this was intentional.

I should have documented that only kmalloc_nolock() ->
kfree_rcu_nolock() is allowed and kmalloc() -> kfree_rcu_nolock()
is not allowed (yet).

> I was able to reproduce this in QEMU with KASAN. The trigger is as
> simple as passing a large (>8KB) kmalloc buffer to the new function.
>
> On Tue, Jun 16, 2026 at 12:06:14AM +0800, Harry Yoo (Oracle) wrote:
>> This commit introduces kfree_rcu_nolock(), a variant of kfree_rcu()
>> designed to be safely called from unknown contexts without falling
>> back to batched processing.
> ...
>> +void kfree_call_rcu_nolock(struct rcu_head *head, void *ptr)
>> +{
>> + struct slab *slab;
>> + struct kmem_cache *s;
>> +
>> + VM_WARN_ON_ONCE(is_vmalloc_addr(ptr) || !virt_to_slab(ptr));
>> +
>> + slab = virt_to_slab(ptr);
>> + s = slab->slab_cache;
>
> The problem: if ptr is a large kmalloc object (> KMALLOC_MAX_CACHE_SIZE,
> which is 8 KB on x86_64), the allocation bypasses SLUB and comes from
> the page allocator. virt_to_slab() returns NULL. VM_WARN_ON_ONCE
> prints a warning but does NOT return, and the next line dereferences
> NULL->slab_cache at offset 0x8.

Since kmalloc_nolock() does not support large kmalloc, the warning
is not supposed to trigger. That is why I added only debug warnings.

> [Reproduction]
>
> I rebuilt the kernel with CONFIG_KASAN=y and added a small late_initcall
> that allocates a 16 KB buffer and passes it to kfree_call_rcu_nolock():
>
> static int __init kfree_rcu_nolock_poc_trigger(void)
> {
> void *p = kmalloc(16384, GFP_KERNEL);
> struct rcu_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
> kfree_call_rcu_nolock(head, p);

As mentioned ealier, kmalloc() -> kfree_rcu_nolock() is not supported.

--
Cheers,
Harry / Hyeonggon

> return 0;
> }
> late_initcall(kfree_rcu_nolock_poc_trigger);
>
> [Crash log — kernel 6.19.0-rc5, CONFIG_KASAN=y, CONFIG_DEBUG_VM=y]
>
> kfree_rcu_nolock PoC: calling kfree_call_rcu_nolock on large obj
> ffff888026c5c000
>
> WARNING: mm/slab_common.c:1271 at kfree_call_rcu_nolock+0x1e/0xc0
> VM_WARN_ON_ONCE(is_vmalloc_addr(ptr) || !virt_to_slab(ptr))
>
> BUG: kernel NULL pointer dereference, address: 0000000000000008
> #PF: supervisor read access in kernel mode
> #PF: error_code(0x0000) - not-present page
>
> RIP: 0010:kfree_call_rcu_nolock+0x5c/0xc0
> Call Trace:
> <TASK>
> poc_trigger_init+0x2a/0x40
> do_one_initcall+0x131/0x730
> kernel_init_freeable+0x471/0x7e0
> kernel_init+0x28/0x300
> ret_from_fork+0x2c/0xc0
> </TASK>
>
> Kernel panic - not syncing: Fatal exception
>
> The crash is at offset 0x5c inside kfree_call_rcu_nolock(), which
> corresponds to `s = slab->slab_cache`. The fault address 0x8 is
> exactly offsetof(struct slab, slab_cache).
>
> [1] https://sashiko.dev/#/patchset/20260615-kfree_rcu_nolock-
> v3-0-70a54f3775bb%40kernel.org
> (Sashiko AI code review — "Null Pointer Dereference", Severity:
> Critical)
>
> Thanks,
> XIAO

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature