]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
slab: Fix obj_ext mistakenly considered NULL due to race condition
authorHao Ge <gehao@kylinos.cn>
Thu, 23 Oct 2025 14:33:13 +0000 (22:33 +0800)
committerVlastimil Babka <vbabka@suse.cz>
Fri, 24 Oct 2025 10:40:13 +0000 (12:40 +0200)
If two competing threads enter alloc_slab_obj_exts(), and the one that
allocates the vector wins the cmpxchg(), the other thread that failed
allocation mistakenly assumes that slab->obj_exts is still empty due to
its own allocation failure. This will then trigger warnings with
CONFIG_MEM_ALLOC_PROFILING_DEBUG checks in the subsequent free path.

Therefore, let's check the result of cmpxchg() to see if marking the
allocation as failed was successful. If it wasn't, check whether the
winning side has succeeded its allocation (it might have been also
marking it as failed) and if yes, return success.

Suggested-by: Harry Yoo <harry.yoo@oracle.com>
Fixes: f7381b911640 ("slab: mark slab->obj_exts allocation failures unconditionally")
Cc: <stable@vger.kernel.org>
Signed-off-by: Hao Ge <gehao@kylinos.cn>
Link: https://patch.msgid.link/20251023143313.1327968-1-hao.ge@linux.dev
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Reviewed-by: Harry Yoo <harry.yoo@oracle.com>
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
mm/slub.c

index 87a1d2f9de0d5def25a40ae57b122bb0e95e736b..d4367f25b20db9f9917e743334b06b8965f9fab6 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2052,9 +2052,9 @@ static inline void mark_objexts_empty(struct slabobj_ext *obj_exts)
        }
 }
 
-static inline void mark_failed_objexts_alloc(struct slab *slab)
+static inline bool mark_failed_objexts_alloc(struct slab *slab)
 {
-       cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL);
+       return cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL) == 0;
 }
 
 static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
@@ -2076,7 +2076,7 @@ static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
 #else /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */
 
 static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) {}
-static inline void mark_failed_objexts_alloc(struct slab *slab) {}
+static inline bool mark_failed_objexts_alloc(struct slab *slab) { return false; }
 static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
                        struct slabobj_ext *vec, unsigned int objects) {}
 
@@ -2124,8 +2124,14 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
                                   slab_nid(slab));
        }
        if (!vec) {
-               /* Mark vectors which failed to allocate */
-               mark_failed_objexts_alloc(slab);
+               /*
+                * Try to mark vectors which failed to allocate.
+                * If this operation fails, there may be a racing process
+                * that has already completed the allocation.
+                */
+               if (!mark_failed_objexts_alloc(slab) &&
+                   slab_obj_exts(slab))
+                       return 0;
 
                return -ENOMEM;
        }