Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/slab: mark alloc tags empty for sheaves allocated with __GFP_NO_OBJ_EXT

alloc_empty_sheaf() allocates sheaves from SLAB_KMALLOC caches using
__GFP_NO_OBJ_EXT to avoid recursion, however it does not mark their
allocation tags empty before freeing, which results in a warning when
CONFIG_MEM_ALLOC_PROFILING_DEBUG is set. Fix this by marking allocation
tags for such sheaves as empty.

The problem was technically introduced in commit 4c0a17e28340 but only
becomes possible to hit with commit 913ffd3a1bf5.

Fixes: 4c0a17e28340 ("slab: prevent recursive kmalloc() in alloc_empty_sheaf()")
Fixes: 913ffd3a1bf5 ("slab: handle kmalloc sheaves bootstrap")
Reported-by: David Wang <00107082@163.com>
Closes: https://lore.kernel.org/all/20260223155128.3849-1-00107082@163.com/
Analyzed-by: Harry Yoo <harry.yoo@oracle.com>
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Reviewed-by: Harry Yoo <harry.yoo@oracle.com>
Tested-by: Harry Yoo <harry.yoo@oracle.com>
Tested-by: David Wang <00107082@163.com>
Link: https://patch.msgid.link/20260225163407.2218712-1-surenb@google.com
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

authored by

Suren Baghdasaryan and committed by
Vlastimil Babka (SUSE)
f3ec502b 021ca6b6

+27 -12
+2
include/linux/gfp_types.h
··· 139 139 * %__GFP_ACCOUNT causes the allocation to be accounted to kmemcg. 140 140 * 141 141 * %__GFP_NO_OBJ_EXT causes slab allocation to have no object extension. 142 + * mark_obj_codetag_empty() should be called upon freeing for objects allocated 143 + * with this flag to indicate that their NULL tags are expected and normal. 142 144 */ 143 145 #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) 144 146 #define __GFP_WRITE ((__force gfp_t)___GFP_WRITE)
+2 -2
mm/slab.h
··· 290 290 291 291 /* Determine object index from a given position */ 292 292 static inline unsigned int __obj_to_index(const struct kmem_cache *cache, 293 - void *addr, void *obj) 293 + void *addr, const void *obj) 294 294 { 295 295 return reciprocal_divide(kasan_reset_tag(obj) - addr, 296 296 cache->reciprocal_size); 297 297 } 298 298 299 299 static inline unsigned int obj_to_index(const struct kmem_cache *cache, 300 - const struct slab *slab, void *obj) 300 + const struct slab *slab, const void *obj) 301 301 { 302 302 if (is_kfence_address(obj)) 303 303 return 0;
+23 -10
mm/slub.c
··· 2041 2041 2042 2042 #ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG 2043 2043 2044 - static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) 2044 + static inline void mark_obj_codetag_empty(const void *obj) 2045 2045 { 2046 - struct slab *obj_exts_slab; 2046 + struct slab *obj_slab; 2047 2047 unsigned long slab_exts; 2048 2048 2049 - obj_exts_slab = virt_to_slab(obj_exts); 2050 - slab_exts = slab_obj_exts(obj_exts_slab); 2049 + obj_slab = virt_to_slab(obj); 2050 + slab_exts = slab_obj_exts(obj_slab); 2051 2051 if (slab_exts) { 2052 2052 get_slab_obj_exts(slab_exts); 2053 - unsigned int offs = obj_to_index(obj_exts_slab->slab_cache, 2054 - obj_exts_slab, obj_exts); 2055 - struct slabobj_ext *ext = slab_obj_ext(obj_exts_slab, 2053 + unsigned int offs = obj_to_index(obj_slab->slab_cache, 2054 + obj_slab, obj); 2055 + struct slabobj_ext *ext = slab_obj_ext(obj_slab, 2056 2056 slab_exts, offs); 2057 2057 2058 2058 if (unlikely(is_codetag_empty(&ext->ref))) { ··· 2090 2090 2091 2091 #else /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ 2092 2092 2093 - static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) {} 2093 + static inline void mark_obj_codetag_empty(const void *obj) {} 2094 2094 static inline bool mark_failed_objexts_alloc(struct slab *slab) { return false; } 2095 2095 static inline void handle_failed_objexts_alloc(unsigned long obj_exts, 2096 2096 struct slabobj_ext *vec, unsigned int objects) {} ··· 2211 2211 * assign slabobj_exts in parallel. In this case the existing 2212 2212 * objcg vector should be reused. 2213 2213 */ 2214 - mark_objexts_empty(vec); 2214 + mark_obj_codetag_empty(vec); 2215 2215 if (unlikely(!allow_spin)) 2216 2216 kfree_nolock(vec); 2217 2217 else ··· 2254 2254 * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that 2255 2255 * the extension for obj_exts is expected to be NULL. 2256 2256 */ 2257 - mark_objexts_empty(obj_exts); 2257 + mark_obj_codetag_empty(obj_exts); 2258 2258 if (allow_spin) 2259 2259 kfree(obj_exts); 2260 2260 else ··· 2311 2311 } 2312 2312 2313 2313 #else /* CONFIG_SLAB_OBJ_EXT */ 2314 + 2315 + static inline void mark_obj_codetag_empty(const void *obj) 2316 + { 2317 + } 2314 2318 2315 2319 static inline void init_slab_obj_exts(struct slab *slab) 2316 2320 { ··· 2787 2783 2788 2784 static void free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf) 2789 2785 { 2786 + /* 2787 + * If the sheaf was created with __GFP_NO_OBJ_EXT flag then its 2788 + * corresponding extension is NULL and alloc_tag_sub() will throw a 2789 + * warning, therefore replace NULL with CODETAG_EMPTY to indicate 2790 + * that the extension for this sheaf is expected to be NULL. 2791 + */ 2792 + if (s->flags & SLAB_KMALLOC) 2793 + mark_obj_codetag_empty(sheaf); 2794 + 2790 2795 kfree(sheaf); 2791 2796 2792 2797 stat(s, SHEAF_FREE);