diff options
Diffstat (limited to 'content/posts/2026-01-15-using-address-sanitizer-with-clang.md')
| -rw-r--r-- | content/posts/2026-01-15-using-address-sanitizer-with-clang.md | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/content/posts/2026-01-15-using-address-sanitizer-with-clang.md b/content/posts/2026-01-15-using-address-sanitizer-with-clang.md new file mode 100644 index 0000000..5ddc149 --- /dev/null +++ b/content/posts/2026-01-15-using-address-sanitizer-with-clang.md | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | --- | ||
| 2 | title: Using Address Sanitizer with clang | ||
| 3 | url: using-address-sanitizer-with-clang.html | ||
| 4 | date: 2026-01-15T16:13:13+02:00 | ||
| 5 | type: post | ||
| 6 | draft: false | ||
| 7 | tags: [] | ||
| 8 | --- | ||
| 9 | |||
| 10 | ## What -fsanitize=address does | ||
| 11 | |||
| 12 | - Enables AddressSanitizer (ASan): a compile and link time instrumentation plus | ||
| 13 | runtime library that detects memory errors. | ||
| 14 | - Detects: out-of-bounds accesses (heap, stack, globals), use-after-free, some | ||
| 15 | use-after-return, double/invalid free, and (on some platforms) leaks. | ||
| 16 | - How it works: instrumented memory accesses are checked against a shadow | ||
| 17 | memory; violations produce an error report with a stack trace and abort the | ||
| 18 | program. | ||
| 19 | - Trade-offs: ~2x runtime slowdown (varies), higher memory use, large virtual | ||
| 20 | address space reservation on 64-bit, and requires linking the ASan runtime | ||
| 21 | (not suitable for production builds). | ||
| 22 | - Usage: compile and link with `-fsanitize=address` (and typically `-g` and | ||
| 23 | `-O0`). Runtime behavior can be tuned via `ASAN_OPTIONS` and | ||
| 24 | symbolization via llvm-symbolizer. | ||
| 25 | |||
| 26 | More about ASan on https://clang.llvm.org/docs/AddressSanitizer.html. | ||
| 27 | |||
| 28 | ## An example how to use it | ||
| 29 | |||
| 30 | ```c | ||
| 31 | #include <stdlib.h> | ||
| 32 | #include <stdio.h> | ||
| 33 | #include <string.h> | ||
| 34 | |||
| 35 | int main(void) { | ||
| 36 | char *p = malloc(10); | ||
| 37 | if (!p) return 1; | ||
| 38 | |||
| 39 | // Out-of-bounds write (heap buffer overflow). | ||
| 40 | strcpy(p, "This string is way too long for the buffer"); | ||
| 41 | |||
| 42 | // Use-after-free (unreachable if program aborts on previous error). | ||
| 43 | free(p); | ||
| 44 | p[0] = 'x'; | ||
| 45 | |||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | ``` | ||
| 49 | |||
| 50 | Now let's compile with proper flags with `clang -O0 -g -fsanitize=address -o | ||
| 51 | main main.c`. | ||
| 52 | |||
| 53 | If you run the binary it should trigger ASan. You can also specify what to | ||
| 54 | show with `ASAN_OPTIONS=detect_leaks=1:verbosity=1:symbolize=1 ./main` and you | ||
| 55 | should see something like this. | ||
| 56 | |||
| 57 | > By using any kind of optimization with `-On` will likely optimize the | ||
| 58 | > problematic code out. But you can never be sure of it. | ||
| 59 | |||
| 60 | ```text | ||
| 61 | MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x02008fff6fff | ||
| 62 | redzone=16 | ||
| 63 | max_redzone=2048 | ||
| 64 | quarantine_size_mb=256M | ||
| 65 | thread_local_quarantine_size_kb=1024K | ||
| 66 | malloc_context_size=30 | ||
| 67 | SHADOW_SCALE: 3 | ||
| 68 | SHADOW_GRANULARITY: 8 | ||
| 69 | SHADOW_OFFSET: 0x00007fff8000 | ||
| 70 | ==28825==Installed the sigaction for signal 11 | ||
| 71 | ==28825==Installed the sigaction for signal 7 | ||
| 72 | ==28825==Installed the sigaction for signal 8 | ||
| 73 | ==28825==T0: FakeStack created: 0x7be4d10f7000 -- 0x7be4d1c00000 stack_size_log: 20; mmapped 11300K, noreserve=0 | ||
| 74 | ==28825==T0: stack [0x7ffcf551c000,0x7ffcf5d1c000) size 0x800000; local=0x7ffcf5d1a664 | ||
| 75 | ==28825==AddressSanitizer Init done | ||
| 76 | ================================================================= | ||
| 77 | ==28825==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c04d25e001a at pc 0x5653c583826a bp 0x7ffcf5d1a5f0 sp 0x7ffcf5d19da8 | ||
| 78 | WRITE of size 43 at 0x7c04d25e001a thread T0 | ||
| 79 | #0 0x5653c5838269 in strcpy (/home/m/Junk/fsanitize/main+0xb7269) (BuildId: 69a0723cc8e27d59eb584f6cc902f6f12915111a) | ||
| 80 | #1 0x5653c5894e13 in main /home/m/Junk/fsanitize/main.c:10:5 | ||
| 81 | #2 0x7fe4d328bbfb in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 | ||
| 82 | #3 0x7fe4d328bcb4 in __libc_start_main@GLIBC_2.2.5 csu/../csu/libc-start.c:360:3 | ||
| 83 | #4 0x5653c57ad360 in _start /builddir/glibc-2.41/csu/../sysdeps/x86_64/start.S:115 | ||
| 84 | |||
| 85 | 0x7c04d25e001a is located 0 bytes after 10-byte region [0x7c04d25e0010,0x7c04d25e001a) | ||
| 86 | allocated by thread T0 here: | ||
| 87 | #0 0x5653c5851ca4 in malloc (/home/m/Junk/fsanitize/main+0xd0ca4) (BuildId: 69a0723cc8e27d59eb584f6cc902f6f12915111a) | ||
| 88 | #1 0x5653c5894de8 in main /home/m/Junk/fsanitize/main.c:6:15 | ||
| 89 | #2 0x7fe4d328bbfb in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 | ||
| 90 | |||
| 91 | SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/m/Junk/fsanitize/main+0xb7269) (BuildId: 69a0723cc8e27d59eb584f6cc902f6f12915111a) in strcpy | ||
| 92 | Shadow bytes around the buggy address: | ||
| 93 | 0x7c04d25dfd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
| 94 | 0x7c04d25dfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
| 95 | 0x7c04d25dfe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
| 96 | 0x7c04d25dff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
| 97 | 0x7c04d25dff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
| 98 | =>0x7c04d25e0000: fa fa 00[02]fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 99 | 0x7c04d25e0080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 100 | 0x7c04d25e0100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 101 | 0x7c04d25e0180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 102 | 0x7c04d25e0200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 103 | 0x7c04d25e0280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa | ||
| 104 | Shadow byte legend (one shadow byte represents 8 application bytes): | ||
| 105 | Addressable: 00 | ||
| 106 | Partially addressable: 01 02 03 04 05 06 07 | ||
| 107 | Heap left redzone: fa | ||
| 108 | Freed heap region: fd | ||
| 109 | Stack left redzone: f1 | ||
| 110 | Stack mid redzone: f2 | ||
| 111 | Stack right redzone: f3 | ||
| 112 | Stack after return: f5 | ||
| 113 | Stack use after scope: f8 | ||
| 114 | Global redzone: f9 | ||
| 115 | Global init order: f6 | ||
| 116 | Poisoned by user: f7 | ||
| 117 | Container overflow: fc | ||
| 118 | Array cookie: ac | ||
| 119 | Intra object redzone: bb | ||
| 120 | ASan internal: fe | ||
| 121 | Left alloca redzone: ca | ||
| 122 | Right alloca redzone: cb | ||
| 123 | ==28825==ABORTING | ||
| 124 | ``` | ||
