5.3.12Build Systems & Toolchain

Address Sanitizer (ASan) — detecting buffer overflows at runtime

2,010 words9 min readdifficulty · medium

WHAT is ASan?

It is not static analysis (it doesn't reason about your code at compile time) and not a separate VM like Valgrind. Instead the compiler injects extra check code around your loads and stores, and a runtime library replaces malloc/free.

WHY do we need it? (the problem)

C/C++ give you raw pointers with no bounds checking. This is fast but dangerous:

int a[4];
a[4] = 42;   // writes 1 element past the end — undefined behaviour

Plain execution often appears to work (you just smashed some adjacent memory), then crashes mysteriously 10,000 lines later. Bugs like this cause ~70% of security CVEs. We need something that fails loudly, immediately, at the exact line.

HOW it works — derive the mechanism from first principles

ASan has two cooperating pieces: the shadow memory and the redzones.

1. Shadow memory (the green/red map)

We want a quick way to ask "is address A valid?". Storing one byte of metadata per application byte would double memory. Instead ASan uses the key observation:

Application memory is aligned in 8-byte granules. Within one granule, the first k bytes are usually valid and the rest poisoned.

So one shadow byte describes 8 application bytes. The mapping is a simple affine function:

The shadow byte value ss encodes how many of the 8 bytes are addressable:

s={0all 8 bytes valid (green)k, 1k7first k bytes valid, rest poisonednegativeall 8 poisoned (redzone, freed, etc.)s = \begin{cases} 0 & \text{all 8 bytes valid (green)}\\ k,\ 1\le k\le 7 & \text{first } k \text{ bytes valid, rest poisoned}\\ \text{negative} & \text{all 8 poisoned (redzone, freed, etc.)} \end{cases}

2. The injected check

For an N-byte access at address AA (assume aligned, N8N\le 8), the compiler turns *A = x into roughly:

shadow = *(char*)(((uintptr_t)A >> 3) + Offset);   // load 1 shadow byte
if (shadow != 0 && ((A & 7) + N - 1) >= shadow)    // last byte index ≥ allowed?
    __asan_report_error(A);                        // poisoned → report & abort
*A = x;                                            // else proceed

3. Redzones around buffers

ASan's runtime makes malloc(n) actually allocate nn + redzones on each side, and poisons those redzone shadow bytes. The same happens for stack arrays (compiler-inserted) and globals. So a[4] lands in a red strip and the check fires.

Figure — Address Sanitizer (ASan) — detecting buffer overflows at runtime

Worked examples

Common mistakes

Connections

  • Valgrind Memcheck — slower, no recompile, shadow-by-emulation instead of instrumentation
  • Undefined Behaviour in C and C++ — the root cause ASan exposes
  • MemorySanitizer (MSan) and UndefinedBehaviorSanitizer (UBSan) — sibling sanitizers
  • Stack vs Heap Memory Layout — why redzones differ per region
  • Compiler Instrumentation and -fsanitize flags
  • Fuzzing (libFuzzer / AFL) — pairs with ASan to reach the buggy paths
Recall Feynman: explain to a 12-year-old

Pretend your toys must stay inside a box. ASan paints a thin red border of "do-not-touch" floor around every box. It also keeps a tiny cheat-sheet (one note per 8 floor tiles) saying which tiles are safe. Every time your robot hand reaches for the floor, it first peeks at the cheat-sheet. Step on green floor → fine. Step on red border → the robot shouts STOP and tells you exactly which toy box you reached past. That's how it catches you reaching outside your box the instant you do it, instead of later when everything's a mess.

Recall Active recall — quick self-test
  1. What formula maps an address to its shadow byte?
  2. Why does one shadow byte cover 8 application bytes?
  3. How does ASan detect use-after-free specifically?
  4. Name one overflow ASan can miss.
What compiler flag enables AddressSanitizer?
-fsanitize=address (in GCC and Clang)
What is the shadow-address mapping formula in ASan?
Shadow(A)=(A3)+Offset\text{Shadow}(A) = (A \gg 3) + \text{Offset}
Why does one shadow byte describe 8 application bytes?
Memory is tracked in 8-byte aligned granules; the shadow byte stores how many of those 8 bytes are addressable (0=all, k=first k, negative=none).
What does a shadow value of 0 mean vs a negative value?
0 = all 8 bytes valid (green); negative = whole granule poisoned/redzone (red, illegal).
What is a "redzone" in ASan?
Poisoned padding inserted before/after each buffer (heap, stack, global) so adjacent overflows hit poisoned shadow and get reported.
How does ASan catch use-after-free?
On free() it poisons the block's shadow and quarantines it (delays reuse), so later access reads poisoned shadow → reported.
For an N-byte access at A, what condition triggers a report?
shadow ≠ 0 AND ((A & 7) + N − 1) ≥ shadow, i.e. the accessed byte reaches into the poisoned part.
Name one class of OOB bug ASan can MISS.
A wild pointer far from the object that lands inside another valid (green) object's memory — no redzone in between.
Is ASan static or runtime detection?
Runtime — it instruments loads/stores at compile time but the checks run during execution on the paths you actually exercise.
Typical ASan overhead?
Roughly 2× CPU slowdown and 2–3× memory use (shadow + redzones + quarantine); it's a debug/test tool, not for production.
Why pair ASan with fuzzing?
ASan only finds bugs on executed paths; fuzzing generates inputs that reach more paths so the redzone checks actually fire.

Concept Map

enabled by

detects

solves

causes

instruments code via

uses

uses

1 byte maps

addressed by

surround

injects

reads

on poison

AddressSanitizer

-fsanitize=address

Buffer overflow, UAF, double-free

C/C++ no bounds checking

~70% security CVEs

GCC / Clang

Shadow memory map

Poisoned redzones

8-byte granule

Shadow A = A>>3 + Offset

Legal buffers

Runtime access check

Report and abort

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, C/C++ me pointer raw hote hain — koi bounds checking nahi. Tum a[4] likh do jabki array sirf 4 elements ka hai, to program crash bhi nahi karega turant; bas pados ki memory kharab kar dega aur baad me kahin random jagah phcatega. Debugging nightmare. ASan (AddressSanitizer) is dard ka solution hai: compile karte waqt -fsanitize=address lagao, aur compiler tumhare har memory access ke aas-paas chhota sa check code daal deta hai.

Idea simple hai: har buffer ke aage-piche ASan redzone (laal patti, "poisoned") laga deta hai, aur ek shadow memory rakhta hai jo batati hai konsi byte safe (green) hai aur konsi illegal (red). Mapping formula hai Shadow(A)=(A3)+Offset\text{Shadow}(A) = (A \gg 3) + \text{Offset} — yaani har 8 application bytes ke liye 1 shadow byte. Shadow value 0 matlab pura granule valid, value kk matlab pehli kk bytes valid, negative matlab pura poisoned. Jab bhi tum memory touch karte ho, ASan pehle shadow check karta hai; red mili to turant report aur crash, exact line ke saath.

Isse heap/stack/global overflow, use-after-free, double-free sab pakde jaate hain — aur woh bhi usi waqt jab galti hoti hai, baad me nahi. Lekin yaad rakho: ASan sirf un paths ke bugs pakdega jo tum actually chala rahe ho (isliye fuzzing ke saath use karo), aur bahut door wala wild pointer kisi dusre valid object me gir jaye to woh miss bhi ho sakta hai. Speed ~2x slow aur memory ~2-3x zyada — isliye ye testing/debug ka tool hai, production ka nahi. Mantra yaad rakho: "Green is GO, Red is DEAD."

Go deeper — visual, from zero

Test yourself — Build Systems & Toolchain

Connections