Hi Alexander/Andrey/Dmitry, For your consideration/review. Thanks! Kuthonuzo Luruo Currently, KASAN may fail to detect concurrent deallocations of the same object due to a race in kasan_slab_free(). This patch makes double-free detection more reliable by atomically setting allocation state for object to KASAN_STATE_QUARANTINE iff current state is KASAN_STATE_ALLOC. Tested using a modified version of the 'slab_test' microbenchmark where allocs occur on CPU 0; then all other CPUs concurrently attempt to free the same object. Signed-off-by: Kuthonuzo Luruo <kuthonuzo.luruo@xxxxxxx> --- mm/kasan/kasan.c | 32 ++++++++++++++++++-------------- mm/kasan/kasan.h | 5 ++--- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index ef2e87b..4fc4e76 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -511,23 +511,28 @@ void kasan_poison_slab_free(struct kmem_cache *cache, void *object) bool kasan_slab_free(struct kmem_cache *cache, void *object) { #ifdef CONFIG_SLAB + struct kasan_alloc_meta *alloc_info; + struct kasan_free_meta *free_info; + /* RCU slabs could be legally used after free within the RCU period */ if (unlikely(cache->flags & SLAB_DESTROY_BY_RCU)) return false; - if (likely(cache->flags & SLAB_KASAN)) { - struct kasan_alloc_meta *alloc_info = - get_alloc_info(cache, object); - struct kasan_free_meta *free_info = - get_free_info(cache, object); - - switch (alloc_info->state) { - case KASAN_STATE_ALLOC: - alloc_info->state = KASAN_STATE_QUARANTINE; - quarantine_put(free_info, cache); - set_track(&free_info->track, GFP_NOWAIT); - kasan_poison_slab_free(cache, object); - return true; + if (unlikely(!(cache->flags & SLAB_KASAN))) + return false; + + alloc_info = get_alloc_info(cache, object); + + if (cmpxchg(&alloc_info->state, KASAN_STATE_ALLOC, + KASAN_STATE_QUARANTINE) == KASAN_STATE_ALLOC) { + free_info = get_free_info(cache, object); + quarantine_put(free_info, cache); + set_track(&free_info->track, GFP_NOWAIT); + kasan_poison_slab_free(cache, object); + return true; + } + + switch (alloc_info->state) { case KASAN_STATE_QUARANTINE: case KASAN_STATE_FREE: pr_err("Double free"); @@ -535,7 +540,6 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object) break; default: break; - } } return false; #else diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index 7da78a6..8c22a96 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h @@ -75,9 +75,8 @@ struct kasan_track { struct kasan_alloc_meta { struct kasan_track track; - u32 state : 2; /* enum kasan_state */ - u32 alloc_size : 30; - u32 reserved; + u32 state; /* enum kasan_state */ + u32 alloc_size; }; struct kasan_free_meta { -- 1.7.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>