kasan: fix prototype author email address
[sfrench/cifs-2.6.git] / mm / kasan / kasan.c
index 405bba487df5ca3c5ce5dce5df0f4e9e14262cde..e13d911251e7f590650e5516f95c6843d0487b31 100644 (file)
@@ -5,7 +5,7 @@
  * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
  *
  * Some code borrowed from https://github.com/xairy/kasan-prototype by
- *        Andrey Konovalov <adech.fo@gmail.com>
+ *        Andrey Konovalov <andreyknvl@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -489,21 +489,17 @@ void kasan_slab_alloc(struct kmem_cache *cache, void *object, gfp_t flags)
        kasan_kmalloc(cache, object, cache->object_size, flags);
 }
 
-static void kasan_poison_slab_free(struct kmem_cache *cache, void *object)
-{
-       unsigned long size = cache->object_size;
-       unsigned long rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
-
-       /* RCU slabs could be legally used after free within the RCU period */
-       if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
-               return;
-
-       kasan_poison_shadow(object, rounded_up_size, KASAN_KMALLOC_FREE);
-}
-
-bool kasan_slab_free(struct kmem_cache *cache, void *object)
+static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
+                             unsigned long ip, bool quarantine)
 {
        s8 shadow_byte;
+       unsigned long rounded_up_size;
+
+       if (unlikely(nearest_obj(cache, virt_to_head_page(object), object) !=
+           object)) {
+               kasan_report_invalid_free(object, ip);
+               return true;
+       }
 
        /* RCU slabs could be legally used after free within the RCU period */
        if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
@@ -511,14 +507,14 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object)
 
        shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object));
        if (shadow_byte < 0 || shadow_byte >= KASAN_SHADOW_SCALE_SIZE) {
-               kasan_report_double_free(cache, object,
-                               __builtin_return_address(1));
+               kasan_report_invalid_free(object, ip);
                return true;
        }
 
-       kasan_poison_slab_free(cache, object);
+       rounded_up_size = round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE);
+       kasan_poison_shadow(object, rounded_up_size, KASAN_KMALLOC_FREE);
 
-       if (unlikely(!(cache->flags & SLAB_KASAN)))
+       if (!quarantine || unlikely(!(cache->flags & SLAB_KASAN)))
                return false;
 
        set_track(&get_alloc_info(cache, object)->free_track, GFP_NOWAIT);
@@ -526,6 +522,11 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object)
        return true;
 }
 
+bool kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip)
+{
+       return __kasan_slab_free(cache, object, ip, true);
+}
+
 void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size,
                   gfp_t flags)
 {
@@ -589,25 +590,29 @@ void kasan_krealloc(const void *object, size_t size, gfp_t flags)
                kasan_kmalloc(page->slab_cache, object, size, flags);
 }
 
-void kasan_poison_kfree(void *ptr)
+void kasan_poison_kfree(void *ptr, unsigned long ip)
 {
        struct page *page;
 
        page = virt_to_head_page(ptr);
 
-       if (unlikely(!PageSlab(page)))
+       if (unlikely(!PageSlab(page))) {
+               if (ptr != page_address(page)) {
+                       kasan_report_invalid_free(ptr, ip);
+                       return;
+               }
                kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page),
                                KASAN_FREE_PAGE);
-       else
-               kasan_poison_slab_free(page->slab_cache, ptr);
+       } else {
+               __kasan_slab_free(page->slab_cache, ptr, ip, false);
+       }
 }
 
-void kasan_kfree_large(const void *ptr)
+void kasan_kfree_large(void *ptr, unsigned long ip)
 {
-       struct page *page = virt_to_page(ptr);
-
-       kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page),
-                       KASAN_FREE_PAGE);
+       if (ptr != page_address(virt_to_head_page(ptr)))
+               kasan_report_invalid_free(ptr, ip);
+       /* The object will be poisoned by page_alloc. */
 }
 
 int kasan_module_alloc(void *addr, size_t size)
@@ -736,6 +741,55 @@ void __asan_unpoison_stack_memory(const void *addr, size_t size)
 }
 EXPORT_SYMBOL(__asan_unpoison_stack_memory);
 
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+       size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+       size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+                       rounded_up_size;
+       size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+       const void *left_redzone = (const void *)(addr -
+                       KASAN_ALLOCA_REDZONE_SIZE);
+       const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+       WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+       kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+                             size - rounded_down_size);
+       kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+                       KASAN_ALLOCA_LEFT);
+       kasan_poison_shadow(right_redzone,
+                       padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+                       KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+       if (unlikely(!stack_top || stack_top > stack_bottom))
+               return;
+
+       kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+       void __asan_set_shadow_##byte(const void *addr, size_t size)    \
+       {                                                               \
+               __memset((void *)addr, 0x##byte, size);                 \
+       }                                                               \
+       EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
+
 #ifdef CONFIG_MEMORY_HOTPLUG
 static int __meminit kasan_mem_notifier(struct notifier_block *nb,
                        unsigned long action, void *data)