slab: Avoid race on slab->obj_exts in alloc_slab_obj_exts
Subsystems (3)
PATCH v3 1/1 slab: Avoid race on slab->obj_exts in alloc_slab_obj_exts
From: Hao Ge <[email protected]>
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.
---
v3: According to Suren's suggestion, simplify the commit message and the code comments.
Thanks for Suren.
v2: Incorporate handling for the scenario where, if mark_failed_objexts_alloc wins the race,
the other process (that previously succeeded in allocation) will lose the race, based on Suren's suggestion. Add Suggested-by: Suren Baghdasaryan <[email protected]>
--- mm/slub.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
mm/slub.c +6 -3
@@ -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)
Signature · 1 line
2.25.1
Quoted 17 lines · Hao Ge
From: Hao Ge <[email protected]>
If two competing threads enter
alloc_slab_obj_exts()and one of them fails to allocate the object extension vector, it might override the validslab->obj_extsallocated by the other thread withOBJEXTS_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_extsatomically usingcmpxchg()to avoidslab->obj_extsoverrides by racing threads.Thanks for Vlastimil and Suren's help with debugging.
Fixes f7381b911640 ("slab: mark slab->obj_exts allocation failures unconditionally")Cc <[email protected]>Suggested-by Suren Baghdasaryan <[email protected]>Signed-off-by Hao Ge <[email protected]>
Quoted 8 lines
---
v3: According to Suren's suggestion, simplify the commit message and the code comments.
Thanks for Suren.v2: Incorporate handling for the scenario where, if
mark_failed_objexts_allocwins the race,
the other process (that previously succeeded in allocation) will lose the race, based on Suren's suggestion. Add Suggested-by: Suren Baghdasaryan <[email protected]>
---
Looks good to me, thanks for fixing this!
Signature · 2 lines
Cheers, Harry / Hyeonggon
Quoted 11 lines · Hao Ge
From: Hao Ge <[email protected]>
If two competing threads enter
alloc_slab_obj_exts()and one of them fails to allocate the object extension vector, it might override the validslab->obj_extsallocated by the other thread withOBJEXTS_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_extsatomically usingcmpxchg()to avoidslab->obj_extsoverrides by racing threads.
Quoted 6 lines
Thanks for Vlastimil and Suren's help with debugging.
Fixes f7381b911640 ("slab: mark slab->obj_exts allocation failures unconditionally")Cc <[email protected]>Suggested-by Suren Baghdasaryan <[email protected]>Signed-off-by Hao Ge <[email protected]>
Added to slab/for-next-fixes, thanks!
Quoted 52 lines
---
v3: According to Suren's suggestion, simplify the commit message and the code comments.
Thanks for Suren.v2: Incorporate handling for the scenario where, if
mark_failed_objexts_allocwins the race,
the other process (that previously succeeded in allocation) will lose the race, based on Suren's suggestion. Add Suggested-by: Suren Baghdasaryan <[email protected]>--- mm/slub.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)mm/slub.c +6
-3@@ -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)
Suren Baghdasaryan
Reviewed-byQuoted 13 lines · Vlastimil Babka
Quoted 11 lines · Hao Ge
From: Hao Ge <[email protected]>
If two competing threads enter
alloc_slab_obj_exts()and one of them fails to allocate the object extension vector, it might override the validslab->obj_extsallocated by the other thread withOBJEXTS_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_extsatomically usingcmpxchg()to avoidslab->obj_extsoverrides by racing threads.
Quoted 6 lines
Quoted 6 lines
Thanks for Vlastimil and Suren's help with debugging.
Fixes f7381b911640 ("slab: mark slab->obj_exts allocation failures unconditionally")Cc <[email protected]>Suggested-by Suren Baghdasaryan <[email protected]>Signed-off-by Hao Ge <[email protected]>
Thanks for the fix, Hao!
Quoted 56 lines
Added to slab/for-next-fixes, thanks!
Quoted 52 lines
---
v3: According to Suren's suggestion, simplify the commit message and the code comments.
Thanks for Suren.v2: Incorporate handling for the scenario where, if
mark_failed_objexts_allocwins the race,
the other process (that previously succeeded in allocation) will lose the race, based on Suren's suggestion. Add Suggested-by: Suren Baghdasaryan <[email protected]>--- mm/slub.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)mm/slub.c +6
-3@@ -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)