|
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
|
|
|
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 |
``` |