kasan: call print_report from kasan_report_invalid_free
[linux-2.6-microblaze.git] / mm / kasan / report.c
index 3ad9624..73348f8 100644 (file)
@@ -86,6 +86,12 @@ __setup("kasan_multi_shot", kasan_set_multi_shot);
 
 static void print_error_description(struct kasan_access_info *info)
 {
+       if (info->type == KASAN_REPORT_INVALID_FREE) {
+               pr_err("BUG: KASAN: double-free or invalid-free in %pS\n",
+                      (void *)info->ip);
+               return;
+       }
+
        pr_err("BUG: KASAN: %s in %pS\n",
                kasan_get_bug_type(info), (void *)info->ip);
        if (info->access_size)
@@ -98,35 +104,57 @@ static void print_error_description(struct kasan_access_info *info)
                        info->access_addr, current->comm, task_pid_nr(current));
 }
 
+#if IS_ENABLED(CONFIG_KASAN_KUNIT_TEST)
+static void update_kunit_status(bool sync)
+{
+       struct kunit *test;
+       struct kunit_resource *resource;
+       struct kunit_kasan_status *status;
+
+       test = current->kunit_test;
+       if (!test)
+               return;
+
+       resource = kunit_find_named_resource(test, "kasan_status");
+       if (!resource) {
+               kunit_set_failure(test);
+               return;
+       }
+
+       status = (struct kunit_kasan_status *)resource->data;
+       WRITE_ONCE(status->report_found, true);
+       WRITE_ONCE(status->sync_fault, sync);
+
+       kunit_put_resource(resource);
+}
+#else
+static void update_kunit_status(bool sync) { }
+#endif
+
 static DEFINE_SPINLOCK(report_lock);
 
-static void start_report(unsigned long *flags)
+static void start_report(unsigned long *flags, bool sync)
 {
-       /*
-        * Make sure we don't end up in loop.
-        */
+       /* Respect the /proc/sys/kernel/traceoff_on_warning interface. */
+       disable_trace_on_warning();
+       /* Update status of the currently running KASAN test. */
+       update_kunit_status(sync);
+       /* Make sure we don't end up in loop. */
        kasan_disable_current();
        spin_lock_irqsave(&report_lock, *flags);
        pr_err("==================================================================\n");
 }
 
-static void end_report(unsigned long *flags, unsigned long addr)
+static void end_report(unsigned long *flags, void *addr)
 {
-       if (!kasan_async_fault_possible())
-               trace_error_report_end(ERROR_DETECTOR_KASAN, addr);
+       if (addr)
+               trace_error_report_end(ERROR_DETECTOR_KASAN,
+                                      (unsigned long)addr);
        pr_err("==================================================================\n");
        add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
        spin_unlock_irqrestore(&report_lock, *flags);
-       if (panic_on_warn && !test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags)) {
-               /*
-                * This thread may hit another WARN() in the panic path.
-                * Resetting this prevents additional WARN() from panicking the
-                * system on this thread.  Other threads are blocked by the
-                * panic_mutex in panic().
-                */
-               panic_on_warn = 0;
+       if (panic_on_warn && !test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
                panic("panic_on_warn set ...\n");
-       }
        if (kasan_arg_fault == KASAN_ARG_FAULT_PANIC)
                panic("kasan.fault=panic set ...\n");
        kasan_enable_current();
@@ -170,9 +198,6 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,
               " which belongs to the cache %s of size %d\n",
                object, cache->name, cache->object_size);
 
-       if (!addr)
-               return;
-
        if (access_addr < object_addr) {
                rel_type = "to the left";
                rel_bytes = object_addr - access_addr;
@@ -261,19 +286,43 @@ static void print_address_description(void *addr, u8 tag)
                void *object = nearest_obj(cache, slab, addr);
 
                describe_object(cache, object, addr, tag);
+               pr_err("\n");
        }
 
        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);
+               pr_err("\n");
+       }
+
+       if (object_is_on_stack(addr)) {
+               /*
+                * Currently, KASAN supports printing frame information only
+                * for accesses to the task's own stack.
+                */
+               kasan_print_address_stack_frame(addr);
+               pr_err("\n");
+       }
+
+       if (is_vmalloc_addr(addr)) {
+               struct vm_struct *va = find_vm_area(addr);
+
+               if (va) {
+                       pr_err("The buggy address belongs to the virtual mapping at\n"
+                              " [%px, %px) created by:\n"
+                              " %pS\n",
+                              va->addr, va->addr + va->size, va->caller);
+                       pr_err("\n");
+
+                       page = vmalloc_to_page(page);
+               }
        }
 
        if (page) {
-               pr_err("The buggy address belongs to the page:\n");
+               pr_err("The buggy address belongs to the physical page:\n");
                dump_page(page, "kasan: bad access detected");
+               pr_err("\n");
        }
-
-       kasan_print_address_stack_frame(addr);
 }
 
 static bool meta_row_is_guilty(const void *row, const void *addr)
@@ -343,124 +392,87 @@ static bool report_enabled(void)
        return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags);
 }
 
-#if IS_ENABLED(CONFIG_KUNIT)
-static void kasan_update_kunit_status(struct kunit *cur_test)
-{
-       struct kunit_resource *resource;
-       struct kunit_kasan_expectation *kasan_data;
-
-       resource = kunit_find_named_resource(cur_test, "kasan_data");
-
-       if (!resource) {
-               kunit_set_failure(cur_test);
-               return;
-       }
-
-       kasan_data = (struct kunit_kasan_expectation *)resource->data;
-       WRITE_ONCE(kasan_data->report_found, true);
-       kunit_put_resource(resource);
-}
-#endif /* IS_ENABLED(CONFIG_KUNIT) */
-
-void kasan_report_invalid_free(void *object, unsigned long ip)
-{
-       unsigned long flags;
-       u8 tag = get_tag(object);
-
-       object = kasan_reset_tag(object);
-
-#if IS_ENABLED(CONFIG_KUNIT)
-       if (current->kunit_test)
-               kasan_update_kunit_status(current->kunit_test);
-#endif /* IS_ENABLED(CONFIG_KUNIT) */
-
-       start_report(&flags);
-       pr_err("BUG: KASAN: double-free or invalid-free in %pS\n", (void *)ip);
-       kasan_print_tags(tag, object);
-       pr_err("\n");
-       print_address_description(object, tag);
-       pr_err("\n");
-       print_memory_metadata(object);
-       end_report(&flags, (unsigned long)object);
-}
-
 #ifdef CONFIG_KASAN_HW_TAGS
 void kasan_report_async(void)
 {
        unsigned long flags;
 
-#if IS_ENABLED(CONFIG_KUNIT)
-       if (current->kunit_test)
-               kasan_update_kunit_status(current->kunit_test);
-#endif /* IS_ENABLED(CONFIG_KUNIT) */
-
-       start_report(&flags);
+       start_report(&flags, false);
        pr_err("BUG: KASAN: invalid-access\n");
        pr_err("Asynchronous mode enabled: no access details available\n");
        pr_err("\n");
        dump_stack_lvl(KERN_ERR);
-       end_report(&flags, 0);
+       end_report(&flags, NULL);
 }
 #endif /* CONFIG_KASAN_HW_TAGS */
 
-static void __kasan_report(unsigned long addr, size_t size, bool is_write,
-                               unsigned long ip)
+static void print_report(struct kasan_access_info *info)
 {
-       struct kasan_access_info info;
-       void *tagged_addr;
-       void *untagged_addr;
-       unsigned long flags;
-
-#if IS_ENABLED(CONFIG_KUNIT)
-       if (current->kunit_test)
-               kasan_update_kunit_status(current->kunit_test);
-#endif /* IS_ENABLED(CONFIG_KUNIT) */
+       void *tagged_addr = info->access_addr;
+       void *untagged_addr = kasan_reset_tag(tagged_addr);
+       u8 tag = get_tag(tagged_addr);
 
-       disable_trace_on_warning();
-
-       tagged_addr = (void *)addr;
-       untagged_addr = kasan_reset_tag(tagged_addr);
-
-       info.access_addr = tagged_addr;
+       print_error_description(info);
        if (addr_has_metadata(untagged_addr))
-               info.first_bad_addr =
-                       kasan_find_first_bad_addr(tagged_addr, size);
-       else
-               info.first_bad_addr = untagged_addr;
-       info.access_size = size;
-       info.is_write = is_write;
-       info.ip = ip;
-
-       start_report(&flags);
-
-       print_error_description(&info);
-       if (addr_has_metadata(untagged_addr))
-               kasan_print_tags(get_tag(tagged_addr), info.first_bad_addr);
+               kasan_print_tags(tag, info->first_bad_addr);
        pr_err("\n");
 
        if (addr_has_metadata(untagged_addr)) {
-               print_address_description(untagged_addr, get_tag(tagged_addr));
-               pr_err("\n");
-               print_memory_metadata(info.first_bad_addr);
+               print_address_description(untagged_addr, tag);
+               print_memory_metadata(info->first_bad_addr);
        } else {
                dump_stack_lvl(KERN_ERR);
        }
+}
+
+void kasan_report_invalid_free(void *ptr, unsigned long ip)
+{
+       unsigned long flags;
+       struct kasan_access_info info;
+
+       start_report(&flags, true);
 
-       end_report(&flags, addr);
+       info.type = KASAN_REPORT_INVALID_FREE;
+       info.access_addr = ptr;
+       info.first_bad_addr = kasan_reset_tag(ptr);
+       info.access_size = 0;
+       info.is_write = false;
+       info.ip = ip;
+
+       print_report(&info);
+
+       end_report(&flags, ptr);
 }
 
 bool kasan_report(unsigned long addr, size_t size, bool is_write,
                        unsigned long ip)
 {
-       unsigned long flags = user_access_save();
-       bool ret = false;
+       bool ret = true;
+       void *ptr = (void *)addr;
+       unsigned long ua_flags = user_access_save();
+       unsigned long irq_flags;
+       struct kasan_access_info info;
 
-       if (likely(report_enabled())) {
-               __kasan_report(addr, size, is_write, ip);
-               ret = true;
+       if (unlikely(!report_enabled())) {
+               ret = false;
+               goto out;
        }
 
-       user_access_restore(flags);
+       start_report(&irq_flags, true);
+
+       info.type = KASAN_REPORT_ACCESS;
+       info.access_addr = ptr;
+       info.first_bad_addr = kasan_find_first_bad_addr(ptr, size);
+       info.access_size = size;
+       info.is_write = is_write;
+       info.ip = ip;
+
+       print_report(&info);
+
+       end_report(&irq_flags, ptr);
+
+out:
+       user_access_restore(ua_flags);
 
        return ret;
 }