From: Greg Kroah-Hartman Date: Wed, 29 Jul 2026 15:47:16 +0000 (+0200) Subject: drop queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc... X-Git-Tag: v6.1.179~1 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=c88dc8b7cfa5fd41c8484ff7bea10ee073559d01;p=thirdparty%2Fkernel%2Fstable-queue.git drop queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch --- diff --git a/queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch b/queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch deleted file mode 100644 index fa64e206da..0000000000 --- a/queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch +++ /dev/null @@ -1,425 +0,0 @@ -From d9e6a7623938968e3752b67e37eaff097e559a54 Mon Sep 17 00:00:00 2001 -From: "Harry Yoo (Oracle)" -Date: Mon, 13 Jul 2026 23:28:52 +0900 -Subject: mm/slab: prevent unbounded recursion in free path with new kmalloc type - -From: Harry Yoo (Oracle) - -commit d9e6a7623938968e3752b67e37eaff097e559a54 upstream. - -Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from -its own slab") avoided recursive allocation of obj_exts from kmalloc -caches of the same size, by bumping the obj_exts array's allocation -size whenever the array size equals the size of the object being -allocated. - -However, as reported by Danielle Costantino and Shakeel Butt, -even slabs from kmalloc caches of different sizes can form a cycle -by allocating obj_exts arrays from each other [1]: - - What happened: a KMALLOC_NORMAL slab's obj_exts array (used by - allocation profiling / memcg accounting) is itself kmalloc()'d from a - KMALLOC_NORMAL cache, so the "slab holds another slab's obj_exts array" - relation can form cycles. With sizeof(struct slabobj_ext) == 16 and - the host's geometry: - - - kmalloc-512 has 64 objects/slab -> array is 64*16 == 1024 bytes, - served from kmalloc-1k; - - kmalloc-1k has 32 objects/slab -> array is 32*16 == 512 bytes, - served from kmalloc-512. - - A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's - obj_exts array. Discarding one frees the other's array, which empties - and discards that slab, which frees the first's array, and so on: - __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> - __free_slab() recurses along the cycle until the stack is exhausted. - -With memory allocation profiling, this allows unbounded recursion -in the free path and led to a stack overflow on a production host in -the Meta fleet [1]: - - BUG: TASK stack guard page was hit - Oops: stack guard page - RIP: 0010:kfree+0x8/0x5d0 - Call Trace: - __free_slab+0x66/0xc0 - kfree+0x3f0/0x5d0 - ... ( ~125x __free_slab <-> kfree ) ... - - do_syscall_64 - -It is proposed [1] to resolve this issue by always serving the obj_exts -array allocation from kmalloc caches (or large kmalloc) of sizes larger -than the object size. However, as pointed out by Vlastimil Babka [2], -this can waste an excessive amount of memory as slabs from large -kmalloc sizes (e.g. kmalloc-8k) generally need obj_exts arrays much -smaller than the object size. - -Therefore, rather than bumping the size, let us take a different -approach; disallow formation of cycles between kmalloc types when -allocating obj_exts arrays. Currently, all obj_exts arrays are served -from normal kmalloc caches. Cycles cannot be created if obj_exts arrays -of normal kmalloc caches are served from a special kmalloc type that can -never have obj_exts arrays. - -To achieve this, create a new kmalloc type called KMALLOC_NO_OBJ_EXT. -KMALLOC_NO_OBJ_EXT caches are created with SLAB_NO_OBJ_EXT flag when -either 1) memory allocation profiling is not permanently disabled, -or 2) kmalloc types with a priority higher than KMALLOC_CGROUP are -aliased with KMALLOC_NORMAL. - -Sheaf bootstrapping for KMALLOC_NO_OBJ_EXT caches now must be deferred -because allocation of a barn can trigger obj_exts array allocation of -normal kmalloc caches when the KMALLOC_NO_OBJ_EXT cache for that size -is not ready yet. For simplicity, perform bootstrapping of sheaves for -all kmalloc caches later. - -Introduce a new slab alloc flag, SLAB_ALLOC_NO_OBJ_EXT, to prevent -allocation of obj_exts arrays, and let kmalloc_slab() override the type -to KMALLOC_NO_OBJ_EXT when specified. Note that kmalloc_type() remains -unchanged because kmalloc_flags() bypasses the kmalloc fastpath. - -Do not pass SLAB_ALLOC_NO_RECURSE to kmalloc_flags() in -alloc_slab_obj_exts() and instead use SLAB_ALLOC_NO_OBJ_EXT only when -the objects are allocated from normal kmalloc caches. While this -prevents unbounded recursive allocation of obj_exts, it allows -KMALLOC_NO_OBJ_EXT caches to have sheaves. - -Since sheaf allocations specify SLAB_ALLOC_NO_RECURSE that prevents -allocation of both sheaves and obj_exts arrays, the recursion depth -is bounded. - -obj_exts arrays for non-kmalloc-normal caches can now have a valid tag. -Do not call mark_obj_codetag_empty() when freeing an obj_exts array to -avoid false warnings. KMALLOC_NO_OBJ_EXT don't need this as they never -allocate those arrays. - -Reported-by: Danielle Costantino -Reported-by: Shakeel Butt -Closes: https://lore.kernel.org/linux-mm/20260625230029.703750-1-shakeel.butt@linux.dev [1] -Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allocation and free paths") -Cc: stable@vger.kernel.org -Link: https://lore.kernel.org/linux-mm/c5c4208d-a6f0-413e-bad9-49be12f12d55@kernel.org [2] -Signed-off-by: Harry Yoo (Oracle) -Reviewed-by: Suren Baghdasaryan -Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org -Signed-off-by: Vlastimil Babka (SUSE) -Signed-off-by: Greg Kroah-Hartman ---- - include/linux/slab.h | 6 +++ - mm/slab.h | 32 ++++++++++++++-- - mm/slab_common.c | 19 +++++++++- - mm/slub.c | 90 ++++++++++++++++---------------------------- - 4 files changed, 85 insertions(+), 62 deletions(-) - -diff --git a/include/linux/slab.h b/include/linux/slab.h -index 08d7b6c9c4d6..3a14df50766a 100644 ---- a/include/linux/slab.h -+++ b/include/linux/slab.h -@@ -708,6 +708,9 @@ enum kmalloc_cache_type { - #endif - #ifndef CONFIG_MEMCG - KMALLOC_CGROUP = KMALLOC_NORMAL, -+#endif -+#ifndef CONFIG_SLAB_OBJ_EXT -+ KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, - #endif - KMALLOC_PARTITION_START = KMALLOC_NORMAL, - KMALLOC_PARTITION_END = KMALLOC_PARTITION_START + KMALLOC_PARTITION_CACHES_NR, -@@ -721,6 +724,9 @@ enum kmalloc_cache_type { - #endif - #ifdef CONFIG_MEMCG - KMALLOC_CGROUP, -+#endif -+#ifdef CONFIG_SLAB_OBJ_EXT -+ KMALLOC_NO_OBJ_EXT, - #endif - NR_KMALLOC_TYPES - }; -diff --git a/mm/slab.h b/mm/slab.h -index 281a65233795..f5e336b6b6b0 100644 ---- a/mm/slab.h -+++ b/mm/slab.h -@@ -22,6 +22,7 @@ - #define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ - #define SLAB_ALLOC_NEW_SLAB 0x02 /* a flag for alloc_slab_obj_exts() */ - #define SLAB_ALLOC_NO_RECURSE 0x04 /* prevent kmalloc() recursion */ -+#define SLAB_ALLOC_NO_OBJ_EXT 0x08 /* prevent obj_exts array allocation */ - - static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) - { -@@ -386,12 +387,17 @@ static inline unsigned int size_index_elem(unsigned int bytes) - * KMALLOC_MAX_CACHE_SIZE and the caller must check that. - */ - static inline struct kmem_cache * --kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token) -+kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token, -+ unsigned int alloc_flags) - { - unsigned int index; -+ enum kmalloc_cache_type type = kmalloc_type(flags, token); -+ -+ if (alloc_flags & SLAB_ALLOC_NO_OBJ_EXT) -+ type = KMALLOC_NO_OBJ_EXT; - - if (!b) -- b = &kmalloc_caches[kmalloc_type(flags, token)]; -+ b = &kmalloc_caches[type]; - if (size <= 192) - index = kmalloc_size_index[size_index_elem(size)]; - else -@@ -426,7 +432,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) - { - if (!is_kmalloc_cache(s)) - return false; -- return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); -+ -+ return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); - } - - bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); -@@ -529,6 +536,25 @@ static inline void metadata_access_disable(void) - kasan_enable_current(); - } - -+/* -+ * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. -+ * -+ * Memory allocation profiling requires obj_exts for all caches. -+ * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types -+ * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. -+ */ -+static inline bool need_kmalloc_no_objext(void) -+{ -+ if (!mem_alloc_profiling_permanently_disabled()) -+ return true; -+ -+ if (!mem_cgroup_kmem_disabled() && -+ (KMALLOC_NORMAL == KMALLOC_RECLAIM)) -+ return true; -+ -+ return false; -+} -+ - #ifdef CONFIG_SLAB_OBJ_EXT - - /* -diff --git a/mm/slab_common.c b/mm/slab_common.c -index b6426d7ceec9..03ecac12cd86 100644 ---- a/mm/slab_common.c -+++ b/mm/slab_common.c -@@ -783,11 +783,15 @@ u8 kmalloc_size_index[24] __ro_after_init = { - size_t kmalloc_size_roundup(size_t size) - { - if (size && size <= KMALLOC_MAX_CACHE_SIZE) { -+ struct kmem_cache *s; -+ - /* - * The flags don't matter since size_index is common to all. - * Neither does the caller for just getting ->object_size. - */ -- return kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0))->object_size; -+ s = kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0), -+ SLAB_ALLOC_DEFAULT); -+ return s->object_size; - } - - /* Above the smaller buckets, size is a multiple of page size. */ -@@ -843,6 +847,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); - #define KMALLOC_PARTITION_NAME(N, sz) - #endif - -+#ifdef CONFIG_SLAB_OBJ_EXT -+#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, -+#else -+#define KMALLOC_NO_OBJ_EXT_NAME(sz) -+#endif -+ - #define INIT_KMALLOC_INFO(__size, __short_size) \ - { \ - .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ -@@ -850,6 +860,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); - KMALLOC_CGROUP_NAME(__short_size) \ - KMALLOC_DMA_NAME(__short_size) \ - KMALLOC_PARTITION_NAME(KMALLOC_PARTITION_CACHES_NR, __short_size) \ -+ KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ - .size = __size, \ - } - -@@ -957,6 +968,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) - return; - } - flags |= SLAB_ACCOUNT; -+ } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { -+ if (!need_kmalloc_no_objext()) { -+ kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; -+ return; -+ } -+ flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; - } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { - flags |= SLAB_CACHE_DMA; - } -diff --git a/mm/slub.c b/mm/slub.c -index abe748b7dddb..0337e60db5ac 100644 ---- a/mm/slub.c -+++ b/mm/slub.c -@@ -2123,42 +2123,6 @@ static inline void init_slab_obj_exts(struct slab *slab) - slab->obj_exts = 0; - } - --/* -- * Calculate the allocation size for slabobj_ext array. -- * -- * When memory allocation profiling is enabled, the obj_exts array -- * could be allocated from the same slab cache it's being allocated for. -- * This would prevent the slab from ever being freed because it would -- * always contain at least one allocated object (its own obj_exts array). -- * -- * To avoid this, increase the allocation size when we detect the array -- * may come from the same cache, forcing it to use a different cache. -- */ --static inline size_t obj_exts_alloc_size(struct kmem_cache *s, -- struct slab *slab, gfp_t gfp) --{ -- size_t sz = sizeof(struct slabobj_ext) * slab->objects; -- struct kmem_cache *obj_exts_cache; -- -- if (sz > KMALLOC_MAX_CACHE_SIZE) -- return sz; -- -- if (!is_kmalloc_normal(s)) -- return sz; -- -- obj_exts_cache = kmalloc_slab(sz, NULL, gfp, __kmalloc_token(0)); -- /* -- * We can't simply compare s with obj_exts_cache, because partitioned kmalloc -- * caches have multiple caches per size, selected by caller address or type. -- * Since caller address or type may differ between kmalloc_slab() and actual -- * allocation, bump size when sizes are equal. -- */ -- if (s->object_size == obj_exts_cache->object_size) -- return obj_exts_cache->object_size + 1; -- -- return sz; --} -- - int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - gfp_t gfp, unsigned int alloc_flags) - { -@@ -2168,14 +2132,18 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - unsigned long new_exts; - unsigned long old_exts; - struct slabobj_ext *vec; -- size_t sz; -+ size_t sz = sizeof(struct slabobj_ext) * slab->objects; - - gfp &= ~OBJCGS_CLEAR_MASK; -- /* Prevent recursive extension vector allocation */ -- alloc_flags |= SLAB_ALLOC_NO_RECURSE; -- alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; -+ /* -+ * In most cases, obj_exts arrays are allocated from normal kmalloc. -+ * However, normal kmalloc caches must allocate them from -+ * KMALLOC_NO_OBJ_EXT caches to prevent recursion. -+ */ -+ if (is_kmalloc_normal(s)) -+ alloc_flags |= SLAB_ALLOC_NO_OBJ_EXT; - -- sz = obj_exts_alloc_size(s, slab, gfp); -+ alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; - - /* This will use kmalloc_nolock() if alloc_flags say so */ - vec = kmalloc_flags(sz, gfp | __GFP_ZERO, alloc_flags, slab_nid(slab)); -@@ -2193,8 +2161,21 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - return -ENOMEM; - } - -- VM_WARN_ON_ONCE(virt_to_slab(vec) != NULL && -- virt_to_slab(vec)->slab_cache == s); -+ if (IS_ENABLED(CONFIG_DEBUG_VM)) { -+ struct kmem_cache *exts_cache; -+ struct slab *exts_slab; -+ -+ exts_slab = virt_to_slab(vec); -+ if (exts_slab) { -+ /* -+ * The vector must be allocated from either normal or -+ * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. -+ */ -+ exts_cache = exts_slab->slab_cache; -+ WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && -+ !(exts_cache->flags & SLAB_NO_OBJ_EXT)); -+ } -+ } - - new_exts = (unsigned long)vec; - #ifdef CONFIG_MEMCG -@@ -2217,7 +2198,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, - * assign slabobj_exts in parallel. In this case the existing - * objcg vector should be reused. - */ -- mark_obj_codetag_empty(vec); - if (unlikely(!allow_spin)) - kfree_nolock(vec); - else -@@ -2253,14 +2233,6 @@ static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) - return; - } - -- /* -- * obj_exts was created with SLAB_ALLOC_NO_RECURSE flag, therefore its -- * corresponding extension will be NULL. alloc_tag_sub() will throw a -- * warning if slab has extensions but the extension of an object is -- * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that -- * the extension for obj_exts is expected to be NULL. -- */ -- mark_obj_codetag_empty(obj_exts); - if (allow_spin) - kfree(obj_exts); - else -@@ -5356,7 +5328,7 @@ void *__do_kmalloc_node(kmem_buckets *b, gfp_t flags, int node, - if (unlikely(!size)) - return ZERO_SIZE_PTR; - -- s = kmalloc_slab(size, b, flags, token); -+ s = kmalloc_slab(size, b, flags, token, ac->alloc_flags); - - ret = slab_alloc_node(s, flags, node, ac); - ret = kasan_kmalloc(s, ret, size, flags); -@@ -5419,7 +5391,9 @@ static void *__kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_f - retry: - if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) - return NULL; -- s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token)); -+ -+ s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token), -+ ac->alloc_flags); - - if (!(s->flags & __CMPXCHG_DOUBLE) && !kmem_cache_debug(s)) - /* -@@ -7979,10 +7953,10 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s) - s->allocflags |= __GFP_RECLAIMABLE; - - /* -- * For KMALLOC_NORMAL caches we enable sheaves later by -- * bootstrap_kmalloc_sheaves() to avoid recursion -+ * For kmalloc caches we enable sheaves later by -+ * bootstrap_kmalloc_sheaves() to avoid recursion. - */ -- if (!is_kmalloc_normal(s)) -+ if (!is_kmalloc_cache(s)) - s->sheaf_capacity = calculate_sheaf_capacity(s, args); - - /* -@@ -8548,7 +8522,7 @@ static void __init bootstrap_kmalloc_sheaves(void) - { - enum kmalloc_cache_type type; - -- for (type = KMALLOC_NORMAL; type <= KMALLOC_PARTITION_END; type++) { -+ for (type = KMALLOC_NORMAL; type < NR_KMALLOC_TYPES; type++) { - for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) { - struct kmem_cache *s = kmalloc_caches[type][idx]; - --- -2.55.0 - diff --git a/queue-7.1/series b/queue-7.1/series index e5459cf89b..6ccb13232f 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -693,4 +693,3 @@ tipc-clear-sock-sk-on-the-failed-insert-path-in-tipc_sk_create.patch vsock-virtio-collapse-receive-queue-under-memory-pressure.patch vxlan-mdb-fix-source-list-corruption-on-a-failed-replace.patch watchdog-s32g_wdt-remove-incorrect-options-in-watchdog_info-struct.patch -mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch