mirror of https://github.com/OpenIPC/firmware.git
311 lines
8.7 KiB
Diff
311 lines
8.7 KiB
Diff
diff -drupN a/mm/kasan/report.c b/mm/kasan/report.c
|
|
--- a/mm/kasan/report.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/mm/kasan/report.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -13,7 +13,9 @@
|
|
*
|
|
*/
|
|
|
|
+#include <linux/bitops.h>
|
|
#include <linux/ftrace.h>
|
|
+#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/printk.h>
|
|
@@ -49,7 +51,13 @@ static const void *find_first_bad_addr(c
|
|
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;
|
|
@@ -94,14 +102,45 @@ static void print_error_description(stru
|
|
case KASAN_USE_AFTER_SCOPE:
|
|
bug_type = "use-after-scope";
|
|
break;
|
|
+ case KASAN_ALLOCA_LEFT:
|
|
+ case KASAN_ALLOCA_RIGHT:
|
|
+ bug_type = "alloca-out-of-bounds";
|
|
+ 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;
|
|
+}
|
|
+
|
|
+static 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)
|
|
@@ -137,12 +176,14 @@ static void kasan_end_report(unsigned lo
|
|
pr_err("==================================================================\n");
|
|
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
|
|
spin_unlock_irqrestore(&report_lock, *flags);
|
|
+ if (panic_on_warn)
|
|
+ panic("panic_on_warn set ...\n");
|
|
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;
|
|
|
|
@@ -153,59 +194,84 @@ static void print_track(struct kasan_tra
|
|
}
|
|
}
|
|
|
|
-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);
|
|
+ dump_stack();
|
|
+ pr_err("\n");
|
|
|
|
- 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");
|
|
+ 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)
|
|
@@ -260,37 +326,74 @@ static void print_shadow_for_address(con
|
|
}
|
|
}
|
|
|
|
+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);
|
|
}
|
|
|
|
kasan_end_report(&flags);
|
|
}
|
|
|
|
+static unsigned long kasan_flags;
|
|
+
|
|
+#define KASAN_BIT_REPORTED 0
|
|
+#define KASAN_BIT_MULTI_SHOT 1
|
|
+
|
|
+bool kasan_save_enable_multi_shot(void)
|
|
+{
|
|
+ return test_and_set_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(kasan_save_enable_multi_shot);
|
|
+
|
|
+void kasan_restore_multi_shot(bool enabled)
|
|
+{
|
|
+ if (!enabled)
|
|
+ clear_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(kasan_restore_multi_shot);
|
|
+
|
|
+static int __init kasan_set_multi_shot(char *str)
|
|
+{
|
|
+ set_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
|
|
+ return 1;
|
|
+}
|
|
+__setup("kasan_multi_shot", kasan_set_multi_shot);
|
|
+
|
|
+static inline bool kasan_report_enabled(void)
|
|
+{
|
|
+ if (current->kasan_depth)
|
|
+ return false;
|
|
+ if (test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
|
|
+ return true;
|
|
+ return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags);
|
|
+}
|
|
+
|
|
void kasan_report(unsigned long addr, size_t size,
|
|
bool is_write, unsigned long ip)
|
|
{
|