kasan: save free stack traces for slab mempools
authorAndrey Konovalov <andreyknvl@google.com>
Tue, 19 Dec 2023 22:28:53 +0000 (23:28 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 29 Dec 2023 19:58:38 +0000 (11:58 -0800)
Make kasan_mempool_poison_object save free stack traces for slab and
kmalloc mempools when the object is freed into the mempool.

Also simplify and rename ____kasan_slab_free to poison_slab_object and do
a few other reability changes.

Link: https://lkml.kernel.org/r/413a7c7c3344fb56809853339ffaabc9e4905e94.1703024586.git.andreyknvl@google.com
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Cc: Alexander Lobakin <alobakin@pm.me>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Breno Leitao <leitao@debian.org>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Evgenii Stepanov <eugenis@google.com>
Cc: Marco Elver <elver@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/kasan.h
mm/kasan/common.c

index f8ebde384bd753e493f4d19b018aa44bd3c41ba0..e636a00e26bac13853b03a709961c7b39e980f0b 100644 (file)
@@ -268,8 +268,9 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip);
  * to reuse them instead of freeing them back to the slab allocator (e.g.
  * mempool).
  *
- * This function poisons a slab allocation without initializing its memory and
- * without putting it into the quarantine (for the Generic mode).
+ * This function poisons a slab allocation and saves a free stack trace for it
+ * without initializing the allocation's memory and without putting it into the
+ * quarantine (for the Generic mode).
  *
  * This function also performs checks to detect double-free and invalid-free
  * bugs and reports them. The caller can use the return value of this function
index 3f4a1ed69e03ab2643cf24f773c0cba9a0c82999..59146886e57de46e1564c4ec99bc29265f544bcb 100644 (file)
@@ -207,8 +207,8 @@ void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache,
        return (void *)object;
 }
 
-static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
-                               unsigned long ip, bool quarantine, bool init)
+static inline bool poison_slab_object(struct kmem_cache *cache, void *object,
+                                     unsigned long ip, bool init)
 {
        void *tagged_object;
 
@@ -221,13 +221,12 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
        if (is_kfence_address(object))
                return false;
 
-       if (unlikely(nearest_obj(cache, virt_to_slab(object), object) !=
-           object)) {
+       if (unlikely(nearest_obj(cache, virt_to_slab(object), object) != object)) {
                kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_INVALID_FREE);
                return true;
        }
 
-       /* RCU slabs could be legally used after free within the RCU period */
+       /* RCU slabs could be legally used after free within the RCU period. */
        if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
                return false;
 
@@ -239,19 +238,18 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
        kasan_poison(object, round_up(cache->object_size, KASAN_GRANULE_SIZE),
                        KASAN_SLAB_FREE, init);
 
-       if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine))
-               return false;
-
        if (kasan_stack_collection_enabled())
                kasan_save_free_info(cache, tagged_object);
 
-       return kasan_quarantine_put(cache, object);
+       return false;
 }
 
 bool __kasan_slab_free(struct kmem_cache *cache, void *object,
                                unsigned long ip, bool init)
 {
-       return ____kasan_slab_free(cache, object, ip, true, init);
+       bool buggy_object = poison_slab_object(cache, object, ip, init);
+
+       return buggy_object ? true : kasan_quarantine_put(cache, object);
 }
 
 static inline bool check_page_allocation(void *ptr, unsigned long ip)
@@ -472,7 +470,7 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip)
        }
 
        slab = folio_slab(folio);
-       return !____kasan_slab_free(slab->slab_cache, ptr, ip, false, false);
+       return !poison_slab_object(slab->slab_cache, ptr, ip, false);
 }
 
 void __kasan_mempool_unpoison_object(void *ptr, size_t size, unsigned long ip)