tools headers UAPI: Sync linux/prctl.h with the kernel sources
[linux-2.6-microblaze.git] / mm / kasan / common.c
index 88f5734..b251676 100644 (file)
@@ -46,6 +46,7 @@ void kasan_set_track(struct kasan_track *track, gfp_t flags)
        track->stack = kasan_save_stack(flags);
 }
 
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 void kasan_enable_current(void)
 {
        current->kasan_depth++;
@@ -55,24 +56,20 @@ void kasan_disable_current(void)
 {
        current->kasan_depth--;
 }
+#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
 
-void kasan_unpoison_range(const void *address, size_t size)
+void __kasan_unpoison_range(const void *address, size_t size)
 {
        unpoison_range(address, size);
 }
 
-static void __kasan_unpoison_stack(struct task_struct *task, const void *sp)
-{
-       void *base = task_stack_page(task);
-       size_t size = sp - base;
-
-       unpoison_range(base, size);
-}
-
+#if CONFIG_KASAN_STACK
 /* Unpoison the entire stack for a task. */
 void kasan_unpoison_task_stack(struct task_struct *task)
 {
-       __kasan_unpoison_stack(task, task_stack_page(task) + THREAD_SIZE);
+       void *base = task_stack_page(task);
+
+       unpoison_range(base, THREAD_SIZE);
 }
 
 /* Unpoison the stack for the current task beyond a watermark sp value. */
@@ -87,8 +84,20 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark)
 
        unpoison_range(base, watermark - base);
 }
+#endif /* CONFIG_KASAN_STACK */
+
+/*
+ * Only allow cache merging when stack collection is disabled and no metadata
+ * is present.
+ */
+slab_flags_t __kasan_never_merge(void)
+{
+       if (kasan_stack_collection_enabled())
+               return SLAB_KASAN;
+       return 0;
+}
 
-void kasan_alloc_pages(struct page *page, unsigned int order)
+void __kasan_alloc_pages(struct page *page, unsigned int order)
 {
        u8 tag;
        unsigned long i;
@@ -102,7 +111,7 @@ void kasan_alloc_pages(struct page *page, unsigned int order)
        unpoison_range(page_address(page), PAGE_SIZE << order);
 }
 
-void kasan_free_pages(struct page *page, unsigned int order)
+void __kasan_free_pages(struct page *page, unsigned int order)
 {
        if (likely(!PageHighMem(page)))
                poison_range(page_address(page),
@@ -116,9 +125,6 @@ void kasan_free_pages(struct page *page, unsigned int order)
  */
 static inline unsigned int optimal_redzone(unsigned int object_size)
 {
-       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
-               return 0;
-
        return
                object_size <= 64        - 16   ? 16 :
                object_size <= 128       - 32   ? 32 :
@@ -129,69 +135,112 @@ static inline unsigned int optimal_redzone(unsigned int object_size)
                object_size <= (1 << 16) - 1024 ? 1024 : 2048;
 }
 
-void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
-                       slab_flags_t *flags)
+void __kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
+                         slab_flags_t *flags)
 {
-       unsigned int orig_size = *size;
-       unsigned int redzone_size;
-       int redzone_adjust;
+       unsigned int ok_size;
+       unsigned int optimal_size;
 
-       /* Add alloc meta. */
-       cache->kasan_info.alloc_meta_offset = *size;
-       *size += sizeof(struct kasan_alloc_meta);
+       /*
+        * SLAB_KASAN is used to mark caches as ones that are sanitized by
+        * KASAN. Currently this flag is used in two places:
+        * 1. In slab_ksize() when calculating the size of the accessible
+        *    memory within the object.
+        * 2. In slab_common.c to prevent merging of sanitized caches.
+        */
+       *flags |= SLAB_KASAN;
 
-       /* Add free meta. */
-       if (IS_ENABLED(CONFIG_KASAN_GENERIC) &&
-           (cache->flags & SLAB_TYPESAFE_BY_RCU || cache->ctor ||
-            cache->object_size < sizeof(struct kasan_free_meta))) {
-               cache->kasan_info.free_meta_offset = *size;
-               *size += sizeof(struct kasan_free_meta);
-       }
+       if (!kasan_stack_collection_enabled())
+               return;
 
-       redzone_size = optimal_redzone(cache->object_size);
-       redzone_adjust = redzone_size - (*size - cache->object_size);
-       if (redzone_adjust > 0)
-               *size += redzone_adjust;
+       ok_size = *size;
 
-       *size = min_t(unsigned int, KMALLOC_MAX_SIZE,
-                       max(*size, cache->object_size + redzone_size));
+       /* Add alloc meta into redzone. */
+       cache->kasan_info.alloc_meta_offset = *size;
+       *size += sizeof(struct kasan_alloc_meta);
 
        /*
-        * If the metadata doesn't fit, don't enable KASAN at all.
+        * If alloc meta doesn't fit, don't add it.
+        * This can only happen with SLAB, as it has KMALLOC_MAX_SIZE equal
+        * to KMALLOC_MAX_CACHE_SIZE and doesn't fall back to page_alloc for
+        * larger sizes.
         */
-       if (*size <= cache->kasan_info.alloc_meta_offset ||
-                       *size <= cache->kasan_info.free_meta_offset) {
+       if (*size > KMALLOC_MAX_SIZE) {
                cache->kasan_info.alloc_meta_offset = 0;
-               cache->kasan_info.free_meta_offset = 0;
-               *size = orig_size;
+               *size = ok_size;
+               /* Continue, since free meta might still fit. */
+       }
+
+       /* Only the generic mode uses free meta or flexible redzones. */
+       if (!IS_ENABLED(CONFIG_KASAN_GENERIC)) {
+               cache->kasan_info.free_meta_offset = KASAN_NO_FREE_META;
                return;
        }
 
-       *flags |= SLAB_KASAN;
+       /*
+        * Add free meta into redzone when it's not possible to store
+        * it in the object. This is the case when:
+        * 1. Object is SLAB_TYPESAFE_BY_RCU, which means that it can
+        *    be touched after it was freed, or
+        * 2. Object has a constructor, which means it's expected to
+        *    retain its content until the next allocation, or
+        * 3. Object is too small.
+        * Otherwise cache->kasan_info.free_meta_offset = 0 is implied.
+        */
+       if ((cache->flags & SLAB_TYPESAFE_BY_RCU) || cache->ctor ||
+           cache->object_size < sizeof(struct kasan_free_meta)) {
+               ok_size = *size;
+
+               cache->kasan_info.free_meta_offset = *size;
+               *size += sizeof(struct kasan_free_meta);
+
+               /* If free meta doesn't fit, don't add it. */
+               if (*size > KMALLOC_MAX_SIZE) {
+                       cache->kasan_info.free_meta_offset = KASAN_NO_FREE_META;
+                       *size = ok_size;
+               }
+       }
+
+       /* Calculate size with optimal redzone. */
+       optimal_size = cache->object_size + optimal_redzone(cache->object_size);
+       /* Limit it with KMALLOC_MAX_SIZE (relevant for SLAB only). */
+       if (optimal_size > KMALLOC_MAX_SIZE)
+               optimal_size = KMALLOC_MAX_SIZE;
+       /* Use optimal size if the size with added metas is not large enough. */
+       if (*size < optimal_size)
+               *size = optimal_size;
 }
 
-size_t kasan_metadata_size(struct kmem_cache *cache)
+size_t __kasan_metadata_size(struct kmem_cache *cache)
 {
+       if (!kasan_stack_collection_enabled())
+               return 0;
        return (cache->kasan_info.alloc_meta_offset ?
                sizeof(struct kasan_alloc_meta) : 0) +
                (cache->kasan_info.free_meta_offset ?
                sizeof(struct kasan_free_meta) : 0);
 }
 
-struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
-                                       const void *object)
+struct kasan_alloc_meta *kasan_get_alloc_meta(struct kmem_cache *cache,
+                                             const void *object)
 {
-       return (void *)object + cache->kasan_info.alloc_meta_offset;
+       if (!cache->kasan_info.alloc_meta_offset)
+               return NULL;
+       return kasan_reset_tag(object) + cache->kasan_info.alloc_meta_offset;
 }
 
-struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
-                                     const void *object)
+#ifdef CONFIG_KASAN_GENERIC
+struct kasan_free_meta *kasan_get_free_meta(struct kmem_cache *cache,
+                                           const void *object)
 {
        BUILD_BUG_ON(sizeof(struct kasan_free_meta) > 32);
-       return (void *)object + cache->kasan_info.free_meta_offset;
+       if (cache->kasan_info.free_meta_offset == KASAN_NO_FREE_META)
+               return NULL;
+       return kasan_reset_tag(object) + cache->kasan_info.free_meta_offset;
 }
+#endif
 
-void kasan_poison_slab(struct page *page)
+void __kasan_poison_slab(struct page *page)
 {
        unsigned long i;
 
@@ -201,16 +250,14 @@ void kasan_poison_slab(struct page *page)
                     KASAN_KMALLOC_REDZONE);
 }
 
-void kasan_unpoison_object_data(struct kmem_cache *cache, void *object)
+void __kasan_unpoison_object_data(struct kmem_cache *cache, void *object)
 {
        unpoison_range(object, cache->object_size);
 }
 
-void kasan_poison_object_data(struct kmem_cache *cache, void *object)
+void __kasan_poison_object_data(struct kmem_cache *cache, void *object)
 {
-       poison_range(object,
-                       round_up(cache->object_size, KASAN_GRANULE_SIZE),
-                       KASAN_KMALLOC_REDZONE);
+       poison_range(object, cache->object_size, KASAN_KMALLOC_REDZONE);
 }
 
 /*
@@ -230,6 +277,9 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object)
 static u8 assign_tag(struct kmem_cache *cache, const void *object,
                        bool init, bool keep_tag)
 {
+       if (IS_ENABLED(CONFIG_KASAN_GENERIC))
+               return 0xff;
+
        /*
         * 1. When an object is kmalloc()'ed, two hooks are called:
         *    kasan_slab_alloc() and kasan_kmalloc(). We assign the
@@ -259,50 +309,32 @@ static u8 assign_tag(struct kmem_cache *cache, const void *object,
 #endif
 }
 
-void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
+void * __must_check __kasan_init_slab_obj(struct kmem_cache *cache,
                                                const void *object)
 {
-       struct kasan_alloc_meta *alloc_info;
-
-       if (!(cache->flags & SLAB_KASAN))
-               return (void *)object;
+       struct kasan_alloc_meta *alloc_meta;
 
-       alloc_info = get_alloc_info(cache, object);
-       __memset(alloc_info, 0, sizeof(*alloc_info));
+       if (kasan_stack_collection_enabled()) {
+               alloc_meta = kasan_get_alloc_meta(cache, object);
+               if (alloc_meta)
+                       __memset(alloc_meta, 0, sizeof(*alloc_meta));
+       }
 
-       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
-               object = set_tag(object,
-                               assign_tag(cache, object, true, false));
+       /* Tag is ignored in set_tag() without CONFIG_KASAN_SW/HW_TAGS */
+       object = set_tag(object, assign_tag(cache, object, true, false));
 
        return (void *)object;
 }
 
-static inline bool shadow_invalid(u8 tag, s8 shadow_byte)
-{
-       if (IS_ENABLED(CONFIG_KASAN_GENERIC))
-               return shadow_byte < 0 ||
-                       shadow_byte >= KASAN_GRANULE_SIZE;
-
-       /* else CONFIG_KASAN_SW_TAGS: */
-       if ((u8)shadow_byte == KASAN_TAG_INVALID)
-               return true;
-       if ((tag != KASAN_TAG_KERNEL) && (tag != (u8)shadow_byte))
-               return true;
-
-       return false;
-}
-
-static 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;
        u8 tag;
        void *tagged_object;
-       unsigned long rounded_up_size;
 
        tag = get_tag(object);
        tagged_object = object;
-       object = reset_tag(object);
+       object = kasan_reset_tag(object);
 
        if (unlikely(nearest_obj(cache, virt_to_head_page(object), object) !=
            object)) {
@@ -314,37 +346,67 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
        if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
                return false;
 
-       shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object));
-       if (shadow_invalid(tag, shadow_byte)) {
+       if (check_invalid_free(tagged_object)) {
                kasan_report_invalid_free(tagged_object, ip);
                return true;
        }
 
-       rounded_up_size = round_up(cache->object_size, KASAN_GRANULE_SIZE);
-       poison_range(object, rounded_up_size, KASAN_KMALLOC_FREE);
+       poison_range(object, cache->object_size, KASAN_KMALLOC_FREE);
+
+       if (!kasan_stack_collection_enabled())
+               return false;
 
-       if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine) ||
-                       unlikely(!(cache->flags & SLAB_KASAN)))
+       if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine))
                return false;
 
        kasan_set_free_info(cache, object, tag);
 
-       quarantine_put(get_free_info(cache, object), cache);
+       return quarantine_put(cache, object);
+}
 
-       return IS_ENABLED(CONFIG_KASAN_GENERIC);
+bool __kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip)
+{
+       return ____kasan_slab_free(cache, object, ip, true);
+}
+
+void __kasan_slab_free_mempool(void *ptr, unsigned long ip)
+{
+       struct page *page;
+
+       page = virt_to_head_page(ptr);
+
+       /*
+        * Even though this function is only called for kmem_cache_alloc and
+        * kmalloc backed mempool allocations, those allocations can still be
+        * !PageSlab() when the size provided to kmalloc is larger than
+        * KMALLOC_MAX_SIZE, and kmalloc falls back onto page_alloc.
+        */
+       if (unlikely(!PageSlab(page))) {
+               if (ptr != page_address(page)) {
+                       kasan_report_invalid_free(ptr, ip);
+                       return;
+               }
+               poison_range(ptr, page_size(page), KASAN_FREE_PAGE);
+       } else {
+               ____kasan_slab_free(page->slab_cache, ptr, ip, false);
+       }
 }
 
-bool kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip)
+static void set_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)
 {
-       return __kasan_slab_free(cache, object, ip, true);
+       struct kasan_alloc_meta *alloc_meta;
+
+       alloc_meta = kasan_get_alloc_meta(cache, object);
+       if (alloc_meta)
+               kasan_set_track(&alloc_meta->alloc_track, flags);
 }
 
-static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
+static void *____kasan_kmalloc(struct kmem_cache *cache, const void *object,
                                size_t size, gfp_t flags, bool keep_tag)
 {
        unsigned long redzone_start;
        unsigned long redzone_end;
-       u8 tag = 0xff;
+       u8 tag;
 
        if (gfpflags_allow_blocking(flags))
                quarantine_reduce();
@@ -356,35 +418,33 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
                                KASAN_GRANULE_SIZE);
        redzone_end = round_up((unsigned long)object + cache->object_size,
                                KASAN_GRANULE_SIZE);
+       tag = assign_tag(cache, object, false, keep_tag);
 
-       if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
-               tag = assign_tag(cache, object, false, keep_tag);
-
-       /* Tag is ignored in set_tag without CONFIG_KASAN_SW_TAGS */
+       /* Tag is ignored in set_tag without CONFIG_KASAN_SW/HW_TAGS */
        unpoison_range(set_tag(object, tag), size);
        poison_range((void *)redzone_start, redzone_end - redzone_start,
                     KASAN_KMALLOC_REDZONE);
 
-       if (cache->flags & SLAB_KASAN)
-               kasan_set_track(&get_alloc_info(cache, object)->alloc_track, flags);
+       if (kasan_stack_collection_enabled())
+               set_alloc_info(cache, (void *)object, flags);
 
        return set_tag(object, tag);
 }
 
-void * __must_check kasan_slab_alloc(struct kmem_cache *cache, void *object,
-                                       gfp_t flags)
+void * __must_check __kasan_slab_alloc(struct kmem_cache *cache,
+                                       void *object, gfp_t flags)
 {
-       return __kasan_kmalloc(cache, object, cache->object_size, flags, false);
+       return ____kasan_kmalloc(cache, object, cache->object_size, flags, false);
 }
 
-void * __must_check kasan_kmalloc(struct kmem_cache *cache, const void *object,
-                               size_t size, gfp_t flags)
+void * __must_check __kasan_kmalloc(struct kmem_cache *cache, const void *object,
+                                       size_t size, gfp_t flags)
 {
-       return __kasan_kmalloc(cache, object, size, flags, true);
+       return ____kasan_kmalloc(cache, object, size, flags, true);
 }
-EXPORT_SYMBOL(kasan_kmalloc);
+EXPORT_SYMBOL(__kasan_kmalloc);
 
-void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
+void * __must_check __kasan_kmalloc_large(const void *ptr, size_t size,
                                                gfp_t flags)
 {
        struct page *page;
@@ -409,7 +469,7 @@ void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
        return (void *)ptr;
 }
 
-void * __must_check kasan_krealloc(const void *object, size_t size, gfp_t flags)
+void * __must_check __kasan_krealloc(const void *object, size_t size, gfp_t flags)
 {
        struct page *page;
 
@@ -419,32 +479,15 @@ void * __must_check kasan_krealloc(const void *object, size_t size, gfp_t flags)
        page = virt_to_head_page(object);
 
        if (unlikely(!PageSlab(page)))
-               return kasan_kmalloc_large(object, size, flags);
+               return __kasan_kmalloc_large(object, size, flags);
        else
-               return __kasan_kmalloc(page->slab_cache, object, size,
+               return ____kasan_kmalloc(page->slab_cache, object, size,
                                                flags, true);
 }
 
-void kasan_poison_kfree(void *ptr, unsigned long ip)
-{
-       struct page *page;
-
-       page = virt_to_head_page(ptr);
-
-       if (unlikely(!PageSlab(page))) {
-               if (ptr != page_address(page)) {
-                       kasan_report_invalid_free(ptr, ip);
-                       return;
-               }
-               poison_range(ptr, page_size(page), KASAN_FREE_PAGE);
-       } else {
-               __kasan_slab_free(page->slab_cache, ptr, ip, false);
-       }
-}
-
-void kasan_kfree_large(void *ptr, unsigned long ip)
+void __kasan_kfree_large(void *ptr, unsigned long ip)
 {
        if (ptr != page_address(virt_to_head_page(ptr)))
                kasan_report_invalid_free(ptr, ip);
-       /* The object will be poisoned by page_alloc. */
+       /* The object will be poisoned by kasan_free_pages(). */
 }