Commit 6ed8bfd2 authored by Hao Ge's avatar Hao Ge Committed by Vlastimil Babka
Browse files

slab: Avoid race on slab->obj_exts in alloc_slab_obj_exts



If two competing threads enter alloc_slab_obj_exts() and one of them
fails to allocate the object extension vector, it might override the
valid slab->obj_exts allocated by the other thread with
OBJEXTS_ALLOC_FAIL. This will cause the thread that lost this race and
expects a valid pointer to dereference a NULL pointer later on.

Update slab->obj_exts atomically using cmpxchg() to avoid
slab->obj_exts overrides by racing threads.

Thanks for Vlastimil and Suren's help with debugging.

Fixes: f7381b91 ("slab: mark slab->obj_exts allocation failures unconditionally")
Cc: <stable@vger.kernel.org>
Suggested-by: default avatarSuren Baghdasaryan <surenb@google.com>
Signed-off-by: default avatarHao Ge <gehao@kylinos.cn>
Reviewed-by: default avatarHarry Yoo <harry.yoo@oracle.com>
Reviewed-by: default avatarSuren Baghdasaryan <surenb@google.com>
Link: https://patch.msgid.link/20251021010353.1187193-1-hao.ge@linux.dev


Signed-off-by: default avatarVlastimil Babka <vbabka@suse.cz>
parent 86f54f9b
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -2054,7 +2054,7 @@ static inline void mark_objexts_empty(struct slabobj_ext *obj_exts)

static inline void mark_failed_objexts_alloc(struct slab *slab)
{
	slab->obj_exts = OBJEXTS_ALLOC_FAIL;
	cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL);
}

static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
@@ -2136,6 +2136,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
#ifdef CONFIG_MEMCG
	new_exts |= MEMCG_DATA_OBJEXTS;
#endif
retry:
	old_exts = READ_ONCE(slab->obj_exts);
	handle_failed_objexts_alloc(old_exts, vec, objects);
	if (new_slab) {
@@ -2145,8 +2146,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
		 * be simply assigned.
		 */
		slab->obj_exts = new_exts;
	} else if ((old_exts & ~OBJEXTS_FLAGS_MASK) ||
		   cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) {
	} else if (old_exts & ~OBJEXTS_FLAGS_MASK) {
		/*
		 * If the slab is already in use, somebody can allocate and
		 * assign slabobj_exts in parallel. In this case the existing
@@ -2158,6 +2158,9 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
		else
			kfree(vec);
		return 0;
	} else if (cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) {
		/* Retry if a racing thread changed slab->obj_exts from under us. */
		goto retry;
	}

	if (allow_spin)