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.lu...@hpe.com>
---
 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

Reply via email to