[PATCH for-next-fixes v2 4/5] mm/slab: fix a deadlock in memcg_alloc_abort_single()

From: Harry Yoo (Meta)

Date: Tue Nov 03 2026 - 09:59:54 EST


When kmalloc_nolock() successfully grabs a slab object but memcg aborts
the allocation, the object is freed via __slab_free(). Calling
__slab_free() in unknown context is not allowed and can lead to a
deadlock.

Free the object via defer_free() when spinning is not allowed.

Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://sashiko.dev/#/patchset/20260610-slab_alloc_flags-v2-0-7190909db118%40kernel.org?part=9
Fixes: af92793e52c3 ("slab: Introduce kmalloc_nolock() and kfree_nolock().")
Signed-off-by: Harry Yoo (Meta) <harry@xxxxxxxxxx>
Reviewed-by: Vlastimil Babka (SUSE) <vbabka@xxxxxxxxxx>
---
mm/slub.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index 48415f24e115..6dfa8ca3c937 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2533,7 +2533,8 @@ static inline void slab_obj_ext_has_codetag_init(void)

#ifdef CONFIG_MEMCG

-static void memcg_alloc_abort_single(struct kmem_cache *s, void *object);
+static void memcg_alloc_abort_single(struct kmem_cache *s, void *object,
+ bool allow_spin);

static __fastpath_inline
bool memcg_slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
@@ -2551,7 +2552,9 @@ bool memcg_slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
return true;

if (likely(size == 1)) {
- memcg_alloc_abort_single(s, *p);
+ bool allow_spin = alloc_flags_allow_spinning(ac->alloc_flags);
+
+ memcg_alloc_abort_single(s, *p, allow_spin);
*p = NULL;
} else {
kmem_cache_free_bulk(s, size, p);
@@ -6663,16 +6666,20 @@ void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
#ifdef CONFIG_MEMCG
/* Do not inline the rare memcg charging failed path into the allocation path */
static noinline
-void memcg_alloc_abort_single(struct kmem_cache *s, void *object)
+void memcg_alloc_abort_single(struct kmem_cache *s, void *object, bool allow_spin)
{
struct slab *slab = virt_to_slab(object);
bool init = slab_want_init_on_free(s);
- bool allow_spin = true;

alloc_tagging_slab_free_hook(s, slab, &object, 1);

- if (likely(slab_free_hook(s, object, init, false, allow_spin)))
+ if (unlikely(!slab_free_hook(s, object, init, false, allow_spin)))
+ return;
+
+ if (likely(allow_spin))
__slab_free(s, slab, object, object, 1, _RET_IP_);
+ else
+ defer_free(s, object);
}
#endif


--
2.54.0