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.

slab: Fix obj_ext mistakenly considered NULL due to race condition

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>

authored by

Hao Ge and committed by
Vlastimil Babka
7f434e1d eecd7cb6

+11 -5
+11 -5
mm/slub.c
··· 2052 2052 } 2053 2053 } 2054 2054 2055 - static inline void mark_failed_objexts_alloc(struct slab *slab) 2055 + static inline bool mark_failed_objexts_alloc(struct slab *slab) 2056 2056 { 2057 - cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL); 2057 + return cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL) == 0; 2058 2058 } 2059 2059 2060 2060 static inline void handle_failed_objexts_alloc(unsigned long obj_exts, ··· 2076 2076 #else /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ 2077 2077 2078 2078 static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) {} 2079 - static inline void mark_failed_objexts_alloc(struct slab *slab) {} 2079 + static inline bool mark_failed_objexts_alloc(struct slab *slab) { return false; } 2080 2080 static inline void handle_failed_objexts_alloc(unsigned long obj_exts, 2081 2081 struct slabobj_ext *vec, unsigned int objects) {} 2082 2082 ··· 2124 2124 slab_nid(slab)); 2125 2125 } 2126 2126 if (!vec) { 2127 - /* Mark vectors which failed to allocate */ 2128 - mark_failed_objexts_alloc(slab); 2127 + /* 2128 + * Try to mark vectors which failed to allocate. 2129 + * If this operation fails, there may be a racing process 2130 + * that has already completed the allocation. 2131 + */ 2132 + if (!mark_failed_objexts_alloc(slab) && 2133 + slab_obj_exts(slab)) 2134 + return 0; 2129 2135 2130 2136 return -ENOMEM; 2131 2137 }