]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
drop queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jul 2026 15:47:16 +0000 (17:47 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jul 2026 15:47:16 +0000 (17:47 +0200)
queue-7.1/mm-slab-prevent-unbounded-recursion-in-free-path-with-new-kmalloc-type.patch [deleted file]
queue-7.1/series

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 (file)
index fa64e20..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-From d9e6a7623938968e3752b67e37eaff097e559a54 Mon Sep 17 00:00:00 2001
-From: "Harry Yoo (Oracle)" <harry@kernel.org>
-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) <harry@kernel.org>
-
-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 ) ...
-   <kernel driver freeing a resource>
-   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 <dcostantino@meta.com>
-Reported-by: Shakeel Butt <shakeel.butt@linux.dev>
-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) <harry@kernel.org>
-Reviewed-by: Suren Baghdasaryan <surenb@google.com>
-Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org
-Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- 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
-
index e5459cf89b924f1a1a214d3742c076d8449c56a1..6ccb13232f00afea5c46fe1cd580c3fa7dfd0583 100644 (file)
@@ -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