From 46a3b88594f45b26dafbc9182a9aaddaba53d924 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 17 Mar 2022 00:53:45 -0700 Subject: [PATCH] Have ASAN errors show origin of memory --- examples/auto-memory-safety-crash.c | 53 ++++++++++++++ examples/auto-memory-safety-crash2.c | 65 +++++++++++++++++ examples/auto-memory-safety-crash3.c | 32 +++++++++ libc/intrin/asan.c | 104 ++++++++++++++++++++++++++- libc/log/showcrashreports.c | 4 +- libc/testlib/leaks.c | 2 +- 6 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 examples/auto-memory-safety-crash.c create mode 100644 examples/auto-memory-safety-crash2.c create mode 100644 examples/auto-memory-safety-crash3.c diff --git a/examples/auto-memory-safety-crash.c b/examples/auto-memory-safety-crash.c new file mode 100644 index 000000000..fa98cad9d --- /dev/null +++ b/examples/auto-memory-safety-crash.c @@ -0,0 +1,53 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/bits/bits.h" +#include "libc/log/log.h" + +/** + * ASAN static memory safety crash example. + * + * make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash.com + * o/dbg/examples/auto-memory-safety-crash.com + * + * You should see: + * + * global redzone 1-byte store at 0x42700d shadow 0x8007ce01 + * ./o/dbg/examples/auto-memory-safety-crash.com + * x + * ........................................GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG + * |0 |0 |0 |0 |5 |-18 |-18 |-18 |-18 + *  f.☼▼ä     f.☼▼ä     f☼▼D  hello                                                 + * 000000400000-000000427000 .text + * 000000427000-000000429000 .data ←address + * 00007fff0000-00008000ffff + * 000080070000-00008008ffff ←shadow + * 0e007fff0000-0e008000ffff + * 100047d20000-100047d3ffff + * 6ffffffe0000-6fffffffffff + * the memory in question belongs to the symbols + * buffer [0x427000,0x42700c] size 13 + * the crash was caused by + * 0x00000000004046f3: __die at libc/log/die.c:40 + * 0x0000000000404aed: __asan_report_store at libc/intrin/asan.c:1183 + * 0x0000000000402552: main at examples/auto-memory-safety-crash.c:27 + * 0x000000000040268d: cosmo at libc/runtime/cosmo.S:64 + * 0x00000000004021ae: _start at libc/crt/crt.S:77 + * + */ + +char buffer[13] = "hello"; + +int main(int argc, char *argv[]) { + ShowCrashReports(); /* not needed but yoinks appropriate symbols */ + int i = 13; + asm("" : "+r"(i)); /* prevent compiler being smart */ + buffer[i] = 1; + return 0; +} diff --git a/examples/auto-memory-safety-crash2.c b/examples/auto-memory-safety-crash2.c new file mode 100644 index 000000000..8c30c3773 --- /dev/null +++ b/examples/auto-memory-safety-crash2.c @@ -0,0 +1,65 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/bits/bits.h" +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" + +/** + * ASAN heap memory safety crash example. + * + * make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash2.com + * o/dbg/examples/auto-memory-safety-crash2.com + * + * You should see: + * + * heap overrun 1-byte store at 0x10008004002d shadow 0x20090000005 + * ./o/dbg/examples/auto-memory-safety-crash2.com + * x + * OOOOOOOOOOOUUUUUUUUUUUUUUUU.............OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO + * |-7 |-6 |-6 |0 |5 |-7 |-7 |-7 |-7 + *    »!@     ÿ▄:Y╩≥= S       hello ∙∙∙∙∙∙∙           ♪     GT◘&@     á+@     »!@   + * 000000400000-00000042b000 .text + * 00000042b000-00000042d000 .data + * 00007fff0000-00008000ffff + * 000080070000-00008008ffff + * 02008fff0000-02009000ffff ←shadow + * 0e007fff0000-0e008000ffff + * 10003ab90000-10003abaffff + * 100080000000-10008000ffff ←address + * 6ffffffe0000-6fffffffffff + * + * the memory was allocated by + * 0x100080040020 64 bytes [dlmalloc] + * 0x100080040030 13 bytes [actual] + * 402608 main + * 402ba0 cosmo + * 4021af _start + * + * the crash was caused by + * 0x0000000000404793: __die at libc/log/die.c:40 + * 0x0000000000404f56: __asan_report_store at libc/intrin/asan.c:1183 + * 0x0000000000402579: main at examples/auto-memory-safety-crash2.c:30 + * 0x000000000040270f: cosmo at libc/runtime/cosmo.S:64 + * 0x00000000004021ae: _start at libc/crt/crt.S:77 + * + */ + +int main(int argc, char *argv[]) { + char *buffer; + ShowCrashReports(); /* not needed but yoinks appropriate symbols */ + buffer = malloc(13); + strcpy(buffer, "hello"); + int i = 13; + asm("" : "+r"(i)); /* prevent compiler being smart */ + buffer[i] = 1; + asm("" : "+r"(buffer)); /* prevent compiler being smart */ + return 0; +} diff --git a/examples/auto-memory-safety-crash3.c b/examples/auto-memory-safety-crash3.c new file mode 100644 index 000000000..616b09a0d --- /dev/null +++ b/examples/auto-memory-safety-crash3.c @@ -0,0 +1,32 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/bits/bits.h" +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" + +/** + * ASAN use-after-free memory safety crash example. + * + * make -j8 MODE=dbg o/dbg/examples/auto-memory-safety-crash3.com + * o/dbg/examples/auto-memory-safety-crash3.com + * + */ + +int main(int argc, char *argv[]) { + char *buffer; + ShowCrashReports(); /* not needed but yoinks appropriate symbols */ + buffer = malloc(13); + strcpy(buffer, "hello"); + free(buffer); + asm("" : "+r"(buffer)); /* prevent compiler being smart */ + buffer[0] = 1; + return 0; +} diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index da393332b..1d57e67ec 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -83,6 +83,8 @@ STATIC_YOINK("_init_asan"); * movq (%addr),%dst */ +#define ASAN_MORGUE_SIZE 128 + #define HOOK(HOOK, IMPL) \ do { \ if (weaken(HOOK)) { \ @@ -137,7 +139,7 @@ struct AsanGlobal { struct AsanMorgue { unsigned i; - void *p[32]; + void *p[ASAN_MORGUE_SIZE]; }; bool __asan_noreentry; @@ -618,6 +620,102 @@ static char *__asan_format_section(char *p, const void *p1, const void *p2, return p; } +static void __asan_report_memory_origin_image(intptr_t a, int z) { + unsigned l, m, r, n, k; + struct SymbolTable *st; + kprintf("%nthe memory belongs to image symbols%n"); + if (weaken(GetSymbolTable)) { + if ((st = weaken(GetSymbolTable)())) { + l = 0; + r = n = st->count; + k = a - st->addr_base; + while (l < r) { + m = (l + r) >> 1; + if (st->symbols[m].y < k) { + l = m + 1; + } else { + r = m; + } + } + for (; l < n; ++l) { + if ((st->symbols[l].x <= k && k <= st->symbols[l].y) || + (st->symbols[l].x <= k + z && k + z <= st->symbols[l].y) || + (k < st->symbols[l].x && st->symbols[l].y < k + z)) { + kprintf("\t%s [%#x,%#x] size %'d%n", st->name_base + st->names[l], + st->addr_base + st->symbols[l].x, + st->addr_base + st->symbols[l].y, + st->symbols[l].y - st->symbols[l].x + 1); + } else { + break; + } + } + } else { + kprintf("\tunknown please supply .com.dbg symbols or set COMDBG%n"); + } + } else { + kprintf("\tunknown please STATIC_YOINK(\"GetSymbolTable\");%n"); + } +} + +struct ReportOriginHeap { + const unsigned char *a; + int z; +}; + +static noasan void OnMemory(void *x, void *y, size_t n, void *a) { + const unsigned char *p = x; + struct ReportOriginHeap *t = a; + if ((p <= t->a && t->a < p + n) || + (p <= t->a + t->z && t->a + t->z < p + n) || + (t->a < p && p + n <= t->a + t->z)) { + kprintf("%p %,lu bytes [dlmalloc]", x, n); + __asan_print_trace(x); + kprintf("\n"); + } +} + +static void __asan_report_memory_origin_heap(const unsigned char *a, int z) { + struct ReportOriginHeap t; + kprintf("%nthe memory was allocated by%n"); + if (weaken(malloc_inspect_all)) { + t.a = a; + t.z = z; + weaken(malloc_inspect_all)(OnMemory, &t); + } else { + kprintf("\tunknown please STATIC_YOINK(\"malloc_inspect_all\");%n"); + } +} + +static void __asan_report_memory_origin(const unsigned char *addr, int size, + signed char kind) { + switch (kind) { + case kAsanStackOverrun: + case kAsanGlobalOverrun: + case kAsanAllocaOverrun: + case kAsanHeapOverrun: + addr -= 1; + size += 1; + break; + case kAsanHeapUnderrun: + case kAsanStackUnderrun: + case kAsanAllocaUnderrun: + case kAsanGlobalUnderrun: + size += 1; + break; + case kAsanGlobalRedzone: + addr -= 1; + size += 2; + break; + default: + break; + } + if (_base <= addr && addr < _end) { + __asan_report_memory_origin_image((intptr_t)addr, size); + } else if (IsAutoFrame((intptr_t)addr >> 16)) { + __asan_report_memory_origin_heap(addr, size); + } +} + nodiscard static __asan_die_f *__asan_report(const void *addr, int size, const char *message, signed char kind) { @@ -705,6 +803,8 @@ nodiscard static __asan_die_f *__asan_report(const void *addr, int size, } *p = 0; kprintf("%s", __fatalbuf); + __asan_report_memory_origin(addr, size, kind); + kprintf("%nthe crash was caused by%n"); return __asan_die(); } @@ -905,7 +1005,7 @@ int __asan_print_trace(void *p) { kprintf(" bad cookie"); return -1; } - kprintf(" %'zu used", n); + kprintf("%n%p %,lu bytes [asan]", (char *)p + 16, n); if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) { kprintf(" (shadow not mapped?!)"); } diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 6eec13085..013cd99b0 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -26,7 +26,9 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/ss.h" -STATIC_YOINK("__die"); +STATIC_YOINK("__die"); /* for backtracing */ +STATIC_YOINK("malloc_inspect_all"); /* for asan memory origin */ +STATIC_YOINK("__get_symbol_by_addr"); /* for asan memory origin */ extern const unsigned char __oncrash_thunks[8][11]; diff --git a/libc/testlib/leaks.c b/libc/testlib/leaks.c index f6c87aca3..3a775cf2f 100644 --- a/libc/testlib/leaks.c +++ b/libc/testlib/leaks.c @@ -45,7 +45,7 @@ static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static int i; if (n) { if (++i < 20) { - kprintf("%p %,d bytes", x, n); + kprintf("%p %,lu bytes [dlmalloc]", x, n); if (IsAsan()) { __asan_print_trace(x); }