kmemleak: account for tagged pointers when calculating pointer range
authorAndrey Konovalov <andreyknvl@google.com>
Thu, 21 Feb 2019 06:19:16 +0000 (22:19 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Feb 2019 17:01:00 +0000 (09:01 -0800)
kmemleak keeps two global variables, min_addr and max_addr, which store
the range of valid (encountered by kmemleak) pointer values, which it
later uses to speed up pointer lookup when scanning blocks.

With tagged pointers this range will get bigger than it needs to be.  This
patch makes kmemleak untag pointers before saving them to min_addr and
max_addr and when performing a lookup.

Link: http://lkml.kernel.org/r/16e887d442986ab87fe87a755815ad92fa431a5f.1550066133.git.andreyknvl@google.com
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Qian Cai <cai@lca.pw>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Evgeniy Stepanov <eugenis@google.com>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kostya Serebryany <kcc@google.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/kmemleak.c
mm/slab.h
mm/slab_common.c
mm/slub.c

index f9d9dc250428142569672a5a5e7c56d8ff949e90..707fa5579f66f1e1e96a5613e50ff74b92417954 100644 (file)
@@ -574,6 +574,7 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
        unsigned long flags;
        struct kmemleak_object *object, *parent;
        struct rb_node **link, *rb_parent;
+       unsigned long untagged_ptr;
 
        object = kmem_cache_alloc(object_cache, gfp_kmemleak_mask(gfp));
        if (!object) {
@@ -619,8 +620,9 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
 
        write_lock_irqsave(&kmemleak_lock, flags);
 
-       min_addr = min(min_addr, ptr);
-       max_addr = max(max_addr, ptr + size);
+       untagged_ptr = (unsigned long)kasan_reset_tag((void *)ptr);
+       min_addr = min(min_addr, untagged_ptr);
+       max_addr = max(max_addr, untagged_ptr + size);
        link = &object_tree_root.rb_node;
        rb_parent = NULL;
        while (*link) {
@@ -1333,6 +1335,7 @@ static void scan_block(void *_start, void *_end,
        unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER);
        unsigned long *end = _end - (BYTES_PER_POINTER - 1);
        unsigned long flags;
+       unsigned long untagged_ptr;
 
        read_lock_irqsave(&kmemleak_lock, flags);
        for (ptr = start; ptr < end; ptr++) {
@@ -1347,7 +1350,8 @@ static void scan_block(void *_start, void *_end,
                pointer = *ptr;
                kasan_enable_current();
 
-               if (pointer < min_addr || pointer >= max_addr)
+               untagged_ptr = (unsigned long)kasan_reset_tag((void *)pointer);
+               if (untagged_ptr < min_addr || untagged_ptr >= max_addr)
                        continue;
 
                /*
index 638ea1b25d394af597a890fc0df673523f940325..3841053187793cce2963ee1cd5f3e680271d21de 100644 (file)
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -438,6 +438,7 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
        flags &= gfp_allowed_mask;
        for (i = 0; i < size; i++) {
                p[i] = kasan_slab_alloc(s, p[i], flags);
+               /* As p[i] might get tagged, call kmemleak hook after KASAN. */
                kmemleak_alloc_recursive(p[i], s->object_size, 1,
                                         s->flags, flags);
        }
index fe524c8d0246ed32b465592997f2a1a530c52859..f9d89c1b5977c03e9e542c9dd48ab38dfa25962e 100644 (file)
@@ -1229,6 +1229,7 @@ void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
        page = alloc_pages(flags, order);
        ret = page ? page_address(page) : NULL;
        ret = kasan_kmalloc_large(ret, size, flags);
+       /* As ret might get tagged, call kmemleak hook after KASAN. */
        kmemleak_alloc(ret, size, 1, flags);
        return ret;
 }
index 4a3d7686902f1972c5107cf97080ddfb20fcd1b0..f5a451c49190134e9e0ce917638fa15194cad34f 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1375,6 +1375,7 @@ static inline void dec_slabs_node(struct kmem_cache *s, int node,
 static inline void *kmalloc_large_node_hook(void *ptr, size_t size, gfp_t flags)
 {
        ptr = kasan_kmalloc_large(ptr, size, flags);
+       /* As ptr might get tagged, call kmemleak hook after KASAN. */
        kmemleak_alloc(ptr, size, 1, flags);
        return ptr;
 }