]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
x86/kasan: Print original address on #GP
authorJann Horn <jannh@google.com>
Wed, 18 Dec 2019 23:11:50 +0000 (00:11 +0100)
committerBorislav Petkov <bp@suse.de>
Tue, 31 Dec 2019 12:15:38 +0000 (13:15 +0100)
Make #GP exceptions caused by out-of-bounds KASAN shadow accesses easier
to understand by computing the address of the original access and
printing that. More details are in the comments in the patch.

This turns an error like this:

  kasan: CONFIG_KASAN_INLINE enabled
  kasan: GPF could be caused by NULL-ptr deref or user memory access
  general protection fault, probably for non-canonical address
      0xe017577ddf75b7dd: 0000 [#1] PREEMPT SMP KASAN PTI

into this:

  general protection fault, probably for non-canonical address
      0xe017577ddf75b7dd: 0000 [#1] PREEMPT SMP KASAN PTI
  KASAN: maybe wild-memory-access in range
      [0x00badbeefbadbee8-0x00badbeefbadbeef]

The hook is placed in architecture-independent code, but is currently
only wired up to the X86 exception handler because I'm not sufficiently
familiar with the address space layout and exception handling mechanisms
on other architectures.

Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: kasan-dev@googlegroups.com
Cc: linux-mm <linux-mm@kvack.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sean Christopherson <sean.j.christopherson@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86-ml <x86@kernel.org>
Link: https://lkml.kernel.org/r/20191218231150.12139-4-jannh@google.com
arch/x86/kernel/dumpstack.c
arch/x86/mm/kasan_init_64.c
include/linux/kasan.h
mm/kasan/report.c

index 8995bf10c97c6a394cae6a846dcef6c2b9fe52ca..ae64ec7f752f401f970d9d3cd3e7722aeb72d60e 100644 (file)
@@ -427,6 +427,8 @@ void die_addr(const char *str, struct pt_regs *regs, long err, long gp_addr)
        int sig = SIGSEGV;
 
        __die_header(str, regs, err);
+       if (gp_addr)
+               kasan_non_canonical_hook(gp_addr);
        if (__die_body(str, regs, err))
                sig = 0;
        oops_end(flags, regs, sig);
index cf5bc37c90ac0bcad59ea7ed7f3954c7f4c8ecb0..763e71abc0fec7d4083feae66f73f49161c781bb 100644 (file)
@@ -288,23 +288,6 @@ static void __init kasan_shallow_populate_pgds(void *start, void *end)
        } while (pgd++, addr = next, addr != (unsigned long)end);
 }
 
-#ifdef CONFIG_KASAN_INLINE
-static int kasan_die_handler(struct notifier_block *self,
-                            unsigned long val,
-                            void *data)
-{
-       if (val == DIE_GPF) {
-               pr_emerg("CONFIG_KASAN_INLINE enabled\n");
-               pr_emerg("GPF could be caused by NULL-ptr deref or user memory access\n");
-       }
-       return NOTIFY_OK;
-}
-
-static struct notifier_block kasan_die_notifier = {
-       .notifier_call = kasan_die_handler,
-};
-#endif
-
 void __init kasan_early_init(void)
 {
        int i;
@@ -341,10 +324,6 @@ void __init kasan_init(void)
        int i;
        void *shadow_cpu_entry_begin, *shadow_cpu_entry_end;
 
-#ifdef CONFIG_KASAN_INLINE
-       register_die_notifier(&kasan_die_notifier);
-#endif
-
        memcpy(early_top_pgt, init_top_pgt, sizeof(early_top_pgt));
 
        /*
index e18fe54969e97ede27c426277d84601d0c6aec6b..5cde9e7c26640ab904b99cab31bac94f9cf54afe 100644 (file)
@@ -228,4 +228,10 @@ static inline void kasan_release_vmalloc(unsigned long start,
                                         unsigned long free_region_end) {}
 #endif
 
+#ifdef CONFIG_KASAN_INLINE
+void kasan_non_canonical_hook(unsigned long addr);
+#else /* CONFIG_KASAN_INLINE */
+static inline void kasan_non_canonical_hook(unsigned long addr) { }
+#endif /* CONFIG_KASAN_INLINE */
+
 #endif /* LINUX_KASAN_H */
index 621782100eaa0f81d930799c6899d598db390550..5ef9f24f566b438a3a8de742fbf20dc0ccca8e75 100644 (file)
@@ -512,3 +512,43 @@ void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned lon
 
        end_report(&flags);
 }
+
+#ifdef CONFIG_KASAN_INLINE
+/*
+ * With CONFIG_KASAN_INLINE, accesses to bogus pointers (outside the high
+ * canonical half of the address space) cause out-of-bounds shadow memory reads
+ * before the actual access. For addresses in the low canonical half of the
+ * address space, as well as most non-canonical addresses, that out-of-bounds
+ * shadow memory access lands in the non-canonical part of the address space.
+ * Help the user figure out what the original bogus pointer was.
+ */
+void kasan_non_canonical_hook(unsigned long addr)
+{
+       unsigned long orig_addr;
+       const char *bug_type;
+
+       if (addr < KASAN_SHADOW_OFFSET)
+               return;
+
+       orig_addr = (addr - KASAN_SHADOW_OFFSET) << KASAN_SHADOW_SCALE_SHIFT;
+       /*
+        * For faults near the shadow address for NULL, we can be fairly certain
+        * that this is a KASAN shadow memory access.
+        * For faults that correspond to shadow for low canonical addresses, we
+        * can still be pretty sure - that shadow region is a fairly narrow
+        * chunk of the non-canonical address space.
+        * But faults that look like shadow for non-canonical addresses are a
+        * really large chunk of the address space. In that case, we still
+        * print the decoded address, but make it clear that this is not
+        * necessarily what's actually going on.
+        */
+       if (orig_addr < PAGE_SIZE)
+               bug_type = "null-ptr-deref";
+       else if (orig_addr < TASK_SIZE)
+               bug_type = "probably user-memory-access";
+       else
+               bug_type = "maybe wild-memory-access";
+       pr_alert("KASAN: %s in range [0x%016lx-0x%016lx]\n", bug_type,
+                orig_addr, orig_addr + KASAN_SHADOW_MASK);
+}
+#endif