]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - mm/kasan/report.c
Merge tag 'for-f2fs-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[mirror_ubuntu-bionic-kernel.git] / mm / kasan / report.c
index ab42a0803f161c6834b1362aefd5ded1990eb04f..beee0e980e2dd3385b007fbcb119f599207dacc0 100644 (file)
@@ -51,7 +51,13 @@ static const void *find_first_bad_addr(const void *addr, size_t size)
        return first_bad_addr;
 }
 
-static void print_error_description(struct kasan_access_info *info)
+static bool addr_has_shadow(struct kasan_access_info *info)
+{
+       return (info->access_addr >=
+               kasan_shadow_to_mem((void *)KASAN_SHADOW_START));
+}
+
+static const char *get_shadow_bug_type(struct kasan_access_info *info)
 {
        const char *bug_type = "unknown-crash";
        u8 *shadow_addr;
@@ -98,12 +104,39 @@ static void print_error_description(struct kasan_access_info *info)
                break;
        }
 
-       pr_err("BUG: KASAN: %s in %pS at addr %p\n",
-               bug_type, (void *)info->ip,
-               info->access_addr);
-       pr_err("%s of size %zu by task %s/%d\n",
-               info->is_write ? "Write" : "Read",
-               info->access_size, current->comm, task_pid_nr(current));
+       return bug_type;
+}
+
+const char *get_wild_bug_type(struct kasan_access_info *info)
+{
+       const char *bug_type = "unknown-crash";
+
+       if ((unsigned long)info->access_addr < PAGE_SIZE)
+               bug_type = "null-ptr-deref";
+       else if ((unsigned long)info->access_addr < TASK_SIZE)
+               bug_type = "user-memory-access";
+       else
+               bug_type = "wild-memory-access";
+
+       return bug_type;
+}
+
+static const char *get_bug_type(struct kasan_access_info *info)
+{
+       if (addr_has_shadow(info))
+               return get_shadow_bug_type(info);
+       return get_wild_bug_type(info);
+}
+
+static void print_error_description(struct kasan_access_info *info)
+{
+       const char *bug_type = get_bug_type(info);
+
+       pr_err("BUG: KASAN: %s in %pS\n",
+               bug_type, (void *)info->ip);
+       pr_err("%s of size %zu at addr %p by task %s/%d\n",
+               info->is_write ? "Write" : "Read", info->access_size,
+               info->access_addr, current->comm, task_pid_nr(current));
 }
 
 static inline bool kernel_or_module_addr(const void *addr)
@@ -144,9 +177,9 @@ static void kasan_end_report(unsigned long *flags)
        kasan_enable_current();
 }
 
-static void print_track(struct kasan_track *track)
+static void print_track(struct kasan_track *track, const char *prefix)
 {
-       pr_err("PID = %u\n", track->pid);
+       pr_err("%s by task %u:\n", prefix, track->pid);
        if (track->stack) {
                struct stack_trace trace;
 
@@ -157,59 +190,84 @@ static void print_track(struct kasan_track *track)
        }
 }
 
-static void kasan_object_err(struct kmem_cache *cache, void *object)
+static struct page *addr_to_page(const void *addr)
 {
-       struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object);
+       if ((addr >= (void *)PAGE_OFFSET) &&
+                       (addr < high_memory))
+               return virt_to_head_page(addr);
+       return NULL;
+}
 
-       dump_stack();
-       pr_err("Object at %p, in cache %s size: %d\n", object, cache->name,
-               cache->object_size);
+static void describe_object_addr(struct kmem_cache *cache, void *object,
+                               const void *addr)
+{
+       unsigned long access_addr = (unsigned long)addr;
+       unsigned long object_addr = (unsigned long)object;
+       const char *rel_type;
+       int rel_bytes;
 
-       if (!(cache->flags & SLAB_KASAN))
+       pr_err("The buggy address belongs to the object at %p\n"
+              " which belongs to the cache %s of size %d\n",
+               object, cache->name, cache->object_size);
+
+       if (!addr)
                return;
 
-       pr_err("Allocated:\n");
-       print_track(&alloc_info->alloc_track);
-       pr_err("Freed:\n");
-       print_track(&alloc_info->free_track);
+       if (access_addr < object_addr) {
+               rel_type = "to the left";
+               rel_bytes = object_addr - access_addr;
+       } else if (access_addr >= object_addr + cache->object_size) {
+               rel_type = "to the right";
+               rel_bytes = access_addr - (object_addr + cache->object_size);
+       } else {
+               rel_type = "inside";
+               rel_bytes = access_addr - object_addr;
+       }
+
+       pr_err("The buggy address is located %d bytes %s of\n"
+              " %d-byte region [%p, %p)\n",
+               rel_bytes, rel_type, cache->object_size, (void *)object_addr,
+               (void *)(object_addr + cache->object_size));
 }
 
-void kasan_report_double_free(struct kmem_cache *cache, void *object,
-                       s8 shadow)
+static void describe_object(struct kmem_cache *cache, void *object,
+                               const void *addr)
 {
-       unsigned long flags;
+       struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object);
 
-       kasan_start_report(&flags);
-       pr_err("BUG: Double free or freeing an invalid pointer\n");
-       pr_err("Unexpected shadow byte: 0x%hhX\n", shadow);
-       kasan_object_err(cache, object);
-       kasan_end_report(&flags);
+       if (cache->flags & SLAB_KASAN) {
+               print_track(&alloc_info->alloc_track, "Allocated");
+               pr_err("\n");
+               print_track(&alloc_info->free_track, "Freed");
+               pr_err("\n");
+       }
+
+       describe_object_addr(cache, object, addr);
 }
 
-static void print_address_description(struct kasan_access_info *info)
+static void print_address_description(void *addr)
 {
-       const void *addr = info->access_addr;
+       struct page *page = addr_to_page(addr);
 
-       if ((addr >= (void *)PAGE_OFFSET) &&
-               (addr < high_memory)) {
-               struct page *page = virt_to_head_page(addr);
-
-               if (PageSlab(page)) {
-                       void *object;
-                       struct kmem_cache *cache = page->slab_cache;
-                       object = nearest_obj(cache, page,
-                                               (void *)info->access_addr);
-                       kasan_object_err(cache, object);
-                       return;
-               }
-               dump_page(page, "kasan: bad access detected");
+       dump_stack();
+       pr_err("\n");
+
+       if (page && PageSlab(page)) {
+               struct kmem_cache *cache = page->slab_cache;
+               void *object = nearest_obj(cache, page, addr);
+
+               describe_object(cache, object, addr);
        }
 
-       if (kernel_or_module_addr(addr)) {
-               if (!init_task_stack_addr(addr))
-                       pr_err("Address belongs to variable %pS\n", addr);
+       if (kernel_or_module_addr(addr) && !init_task_stack_addr(addr)) {
+               pr_err("The buggy address belongs to the variable:\n");
+               pr_err(" %pS\n", addr);
+       }
+
+       if (page) {
+               pr_err("The buggy address belongs to the page:\n");
+               dump_page(page, "kasan: bad access detected");
        }
-       dump_stack();
 }
 
 static bool row_is_guilty(const void *row, const void *guilty)
@@ -264,31 +322,34 @@ static void print_shadow_for_address(const void *addr)
        }
 }
 
+void kasan_report_double_free(struct kmem_cache *cache, void *object,
+                               void *ip)
+{
+       unsigned long flags;
+
+       kasan_start_report(&flags);
+       pr_err("BUG: KASAN: double-free or invalid-free in %pS\n", ip);
+       pr_err("\n");
+       print_address_description(object);
+       pr_err("\n");
+       print_shadow_for_address(object);
+       kasan_end_report(&flags);
+}
+
 static void kasan_report_error(struct kasan_access_info *info)
 {
        unsigned long flags;
-       const char *bug_type;
 
        kasan_start_report(&flags);
 
-       if (info->access_addr <
-                       kasan_shadow_to_mem((void *)KASAN_SHADOW_START)) {
-               if ((unsigned long)info->access_addr < PAGE_SIZE)
-                       bug_type = "null-ptr-deref";
-               else if ((unsigned long)info->access_addr < TASK_SIZE)
-                       bug_type = "user-memory-access";
-               else
-                       bug_type = "wild-memory-access";
-               pr_err("BUG: KASAN: %s on address %p\n",
-                       bug_type, info->access_addr);
-               pr_err("%s of size %zu by task %s/%d\n",
-                       info->is_write ? "Write" : "Read",
-                       info->access_size, current->comm,
-                       task_pid_nr(current));
+       print_error_description(info);
+       pr_err("\n");
+
+       if (!addr_has_shadow(info)) {
                dump_stack();
        } else {
-               print_error_description(info);
-               print_address_description(info);
+               print_address_description((void *)info->access_addr);
+               pr_err("\n");
                print_shadow_for_address(info->first_bad_addr);
        }