An AddressSanitizer report starts with the error type, then shows the bad access and up to three stack traces: where the bad access happened, where the memory was freed, and where it was first allocated. Read the headline, then the first stack, then the other two for use-after-free bugs.
The parts of a report
Per the Clang documentation, each report names the error type, such as heap-use-after-free, and the faulting address. It shows whether the access was a read or a write, its size, and the thread. Then come stack traces with file and line for the bad access, the free, and the original allocation.
Here is a shortened, made-up example of the shape:
ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010
READ of size 4 at 0x602000000010 thread T0
#0 read_field parser.c:88
#1 parse_record parser.c:131
freed by thread T0 here:
#0 free
#1 drop_record parser.c:61
previously allocated by thread T0 here:
#0 malloc
#1 new_record parser.c:40
The file names and line numbers are invented for illustration.
Step 1: read the headline
The error type tells you what kind of bug to look for. Heap-buffer-overflow means an index or length is wrong. Heap-use-after-free means two pieces of code disagree on who owns the memory. Stack-buffer-overflow and global-buffer-overflow point at local arrays and static data. Double-free and invalid free are ownership bugs again.
Step 2: the first stack is the symptom
The top stack is where the program noticed. It is often not where the mistake was made. In the example, read_field reads freed memory, but the real fault is that something freed the record too early.
Step 3: the free and allocation stacks are the story
For use-after-free, compare the "freed by" stack with the "allocated by" stack. Ask which function owned this memory, and why a second function still held a pointer to it. The fix is usually in how ownership is passed, not in the line that crashed.
For overflows, the report also describes which allocation the address falls near. A read that lands a few bytes past a buffer usually means an off-by-one in a length check.
Step 4: make it readable
If you see raw addresses and no file lines, symbolization is missing. The docs say you need llvm-symbolizer, set through ASAN_SYMBOLIZER_PATH or found on the path. Build with -g, and on macOS you may need dsymutil. Also keep -fno-omit-frame-pointer for sound traces.
Step 5: confirm with the saved input
If the report came from a fuzzer, replay the single saved input and confirm you get the same report. Then fix the code, re-run that input, and add it to your regression corpus. See our post on triaging crashes for the full routine.
Do not treat the report as the exploit
A report says memory was misused. It does not say how serious the bug is. Whether an attacker can control the bytes involved, or reach the code from outside, is a separate question, and we cover it in a post on severity.
Quick check
Which stack trace should I read first?
The top one, for the symptom. For use-after-free then read the free and allocation stacks, because the cause is usually there.
My report has no line numbers. What is missing?
Symbolization. Build with debug info and make sure llvm-symbolizer is available to the runtime.
Sources
Last reviewed: 19 October 2026.