* [PATCH] mm/slub: Flush virtual-slab work before cache destruction
@ 2026-11-13 22:13 Felix Hoffmann
2026-11-14 3:38 ` Harry Yoo
2026-11-14 22:14 ` sashiko-bot
0 siblings, 2 replies; 4+ messages in thread
From: Felix Hoffmann @ 2026-11-13 22:13 UTC (permalink / raw)
To: Jann Horn, Matteo Rizzo, Tamas Koczka, Vlastimil Babka, linux-mm
Cc: Kees Cook, Harry Yoo, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, linux-hardening, linux-kernel
Under CONFIG_SLAB_VIRTUAL, freeing a slab queues it to
slub_tlbflush_work. The worker later follows slab->slab_cache to take the
cache's virtual-slab lock and update its lists.
__kmem_cache_shutdown() can report success after the last slabs have been
removed from the cache's accounting but before that deferred work runs.
The cache can then be released while queued slabs still refer to it. When
the worker runs, it dereferences the freed kmem_cache and corrupts its
lists.
Flush the virtual-slab worker after confirming that every cache node is
empty and before allowing cache destruction to proceed. This drains both
queued and running work that can still reference the cache.
Fixes: 0d6f38b78adda ("mm/slub: allocate slabs from virtual memory")
Signed-off-by: Felix Hoffmann <f3lix.dev@gmx.de>
---
Tested on x86-64 with KCSAN enabled:
- on the unmodified SLAB_VIRTUAL feature kernel, a stress module that
repeatedly created, filled, emptied and destroyed a non-mergeable cache
triggered list corruption in slub_tlbflush_worker() and panicked the
kernel almost immediately
- with this change, the identical module completed 10,000 cache
create/destroy cycles (81.92 million allocation/free pairs) without a
BUG, warning, KCSAN report or panic
- the fixed KCSAN kernel and the external stress module build with W=1
The reproducer and complete console logs are available privately on request.
mm/slub.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/mm/slub.c b/mm/slub.c
index b4c64b2bbd334..dbfd42b31a0a2 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -8541,6 +8541,11 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
if (n->nr_partial || node_nr_slabs(n))
return 1;
}
+
+#ifdef CONFIG_SLAB_VIRTUAL
+ /* The TLB flush worker still dereferences slab->slab_cache. */
+ kthread_flush_work(&slub_tlbflush_work);
+#endif
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] mm/slub: Flush virtual-slab work before cache destruction
2026-11-13 22:13 [PATCH] mm/slub: Flush virtual-slab work before cache destruction Felix Hoffmann
@ 2026-11-14 3:38 ` Harry Yoo
2026-11-14 10:09 ` Felix Hoffmann
2026-11-14 22:14 ` sashiko-bot
1 sibling, 1 reply; 4+ messages in thread
From: Harry Yoo @ 2026-11-14 3:38 UTC (permalink / raw)
To: Felix Hoffmann
Cc: Jann Horn, Matteo Rizzo, Tamas Koczka, Vlastimil Babka, linux-mm,
Kees Cook, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, linux-hardening, linux-kernel
Hi Felix,
CONFIG_SLAB_VIRTUAL is not in the upstream kernel.
Maybe you sent a patch for a downstream tree to the upstream?
On Fri, 13 Nov 2026 23:13:30 +0100, Felix Hoffmann wrote:
> Under CONFIG_SLAB_VIRTUAL, freeing a slab queues it to
> slub_tlbflush_work. The worker later follows slab->slab_cache to take the
> cache's virtual-slab lock and update its lists.
>
> __kmem_cache_shutdown() can report success after the last slabs have been
> removed from the cache's accounting but before that deferred work runs.
> The cache can then be released while queued slabs still refer to it. When
> the worker runs, it dereferences the freed kmem_cache and corrupts its
> lists.
>
> Flush the virtual-slab worker after confirming that every cache node is
> empty and before allowing cache destruction to proceed. This drains both
> queued and running work that can still reference the cache.
>
> Fixes: 0d6f38b78adda ("mm/slub: allocate slabs from virtual memory")
> Signed-off-by: Felix Hoffmann <f3lix.dev@gmx.de>
> ---
> Tested on x86-64 with KCSAN enabled:
>
> - on the unmodified SLAB_VIRTUAL feature kernel, a stress module that
> repeatedly created, filled, emptied and destroyed a non-mergeable cache
> triggered list corruption in slub_tlbflush_worker() and panicked the
> kernel almost immediately
> - with this change, the identical module completed 10,000 cache
> create/destroy cycles (81.92 million allocation/free pairs) without a
> BUG, warning, KCSAN report or panic
> - the fixed KCSAN kernel and the external stress module build with W=1
>
> The reproducer and complete console logs are available privately on request.
> mm/slub.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/mm/slub.c b/mm/slub.c
> index b4c64b2bbd334..dbfd42b31a0a2 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -8541,6 +8541,11 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
> if (n->nr_partial || node_nr_slabs(n))
> return 1;
> }
> +
> +#ifdef CONFIG_SLAB_VIRTUAL
> + /* The TLB flush worker still dereferences slab->slab_cache. */
> + kthread_flush_work(&slub_tlbflush_work);
> +#endif
> return 0;
> }
>
> --
> 2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] mm/slub: Flush virtual-slab work before cache destruction
2026-11-14 3:38 ` Harry Yoo
@ 2026-11-14 10:09 ` Felix Hoffmann
0 siblings, 0 replies; 4+ messages in thread
From: Felix Hoffmann @ 2026-11-14 10:09 UTC (permalink / raw)
To: Harry Yoo
Cc: Jann Horn, Matteo Rizzo, Tamas Koczka, Vlastimil Babka, linux-mm,
Kees Cook, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin, linux-hardening, linux-kernel
Hi Harry,
Thank you for catching this. You're right: CONFIG_SLAB_VIRTUAL is not in
mainline.
This patch is against the public, out-of-tree SLAB_VIRTUAL feature branch:
https://github.com/thejh/linux/tree/slub-virtual-v7.2
I tested it on commit 699594e8888d (slub-virtual-v7.2-rc5), and confirmed
that the same issue is present in slub-virtual-v7.2. I intended the fix
for the feature authors to fold into a future revision of their series.
I failed to state that dependency in the subject and body, so the
standalone email misleadingly looked like a patch against upstream. That
was my mistake, sorry for the confusion.
Jann, Matteo, and Tamas, please let me know if you would prefer the fix
in a differently labeled or rebased form.
Thanks,
Felix
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] mm/slub: Flush virtual-slab work before cache destruction
2026-11-13 22:13 [PATCH] mm/slub: Flush virtual-slab work before cache destruction Felix Hoffmann
2026-11-14 3:38 ` Harry Yoo
@ 2026-11-14 22:14 ` sashiko-bot
1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-14 22:14 UTC (permalink / raw)
To: Felix Hoffmann, Jann Horn, Matteo Rizzo, Tamas Koczka,
Vlastimil Babka, linux-mm
Cc: Kees Cook, Harry Yoo, Andrew Morton, Hao Li, Christoph Lameter,
David Rientjes, Roman Gushchin
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] mm/slub: undeclared slub_tlbflush_work under nonexistent
CONFIG_SLAB_VIRTUAL
- [Medium] mm/slub: __kmem_cache_shutdown() flushes work before RCU slab
free barrier
- [Low] mm/slub: unresolvable Fixes commit 0d6f38b78adda in commit
message
--
commit eea96dc753bff553cd7a4356f0451c5fa1889295
Author: Felix Hoffmann <f3lix.dev@gmx.de>
> mm/slub: Flush virtual-slab work before cache destruction
>
> Under CONFIG_SLAB_VIRTUAL, freeing a slab queues it to
> slub_tlbflush_work. The worker later follows slab->slab_cache to take the
> cache's virtual-slab lock and update its lists.
>
> __kmem_cache_shutdown() can report success after the last slabs have been
> removed from the cache's accounting but before that deferred work runs.
> The cache can then be released while queued slabs still refer to it. When
> the worker runs, it dereferences the freed kmem_cache and corrupts its
> lists.
>
> Flush the virtual-slab worker after confirming that every cache node is
> empty and before allowing cache destruction to proceed. This drains both
> queued and running work that can still reference the cache.
>
> Fixes: 0d6f38b78adda ("mm/slub: allocate slabs from virtual memory")
> Signed-off-by: Felix Hoffmann <f3lix.dev@gmx.de>
[Severity: Low]
The Fixes: trailer references commit 0d6f38b78adda ("mm/slub: allocate
slabs from virtual memory"), which cannot be resolved in the repository
or tracked maintainer trees.
> diff --git a/mm/slub.c b/mm/slub.c
> index a151268b97263..ec432cff15f58 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -8298,6 +8298,11 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
> if (n->nr_partial || node_nr_slabs(n))
> return 1;
> }
> +
> +#ifdef CONFIG_SLAB_VIRTUAL
> + /* The TLB flush worker still dereferences slab->slab_cache. */
> + kthread_flush_work(&slub_tlbflush_work);
> +#endif
[Severity: Medium]
Is this patch missing the prerequisite virtual-slab changes?
Neither CONFIG_SLAB_VIRTUAL nor slub_tlbflush_work exists in the tree,
and mm/slub.c does not include linux/kthread.h for kthread_flush_work().
As a result, this block is unreachable dead code in normal builds and
fails to compile if CONFIG_SLAB_VIRTUAL is defined.
[Severity: Medium]
Can destroying a SLAB_TYPESAFE_BY_RCU cache still race with
slub_tlbflush_work and cause a use-after-free on slab->slab_cache?
During __kmem_cache_shutdown(), discard_slab() decrements the node slab
count via dec_slabs_node() before calling free_slab(). When
SLAB_TYPESAFE_BY_RCU is set, free_slab() defers __free_slab() via
call_rcu(&slab->rcu_head, rcu_free_slab):
mm/slub.c:free_slab() {
...
if (unlikely(s->flags & SLAB_TYPESAFE_BY_RCU))
call_rcu(&slab->rcu_head, rcu_free_slab);
else
__free_slab(s, slab, true);
}
Because node_nr_slabs(n) is already zero, __kmem_cache_shutdown() flushes
slub_tlbflush_work (while holding cpus_read_lock and slab_mutex) before
those rcu_free_slab() callbacks have run. kmem_cache_destroy() only
waits for rcu_free_slab() afterwards, and then immediately frees the
kmem_cache:
mm/slab_common.c:kmem_cache_destroy() {
...
if (s->flags & SLAB_TYPESAFE_BY_RCU)
rcu_barrier();
kmem_cache_release(s, sysfs_ready);
}
Slabs freed by rcu_free_slab() during rcu_barrier() are queued to
slub_tlbflush_work after the flush in __kmem_cache_shutdown() has already
completed, so the worker can still dereference slab->slab_cache after
kmem_cache_release() frees s. Should kthread_flush_work() be called in
kmem_cache_destroy() after rcu_barrier() (and outside cpus_read_lock and
slab_mutex) instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113221330.162567-1-f3lix.dev@gmx.de?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-14 22:14 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-13 22:13 [PATCH] mm/slub: Flush virtual-slab work before cache destruction Felix Hoffmann
2026-11-14 3:38 ` Harry Yoo
2026-11-14 10:09 ` Felix Hoffmann
2026-11-14 22:14 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.