AddressSanitizer (ASan) adalah tool dinamis yang menginstrumentasi program C saat kompilasi untuk mendeteksi bug memori pada saat runtime: buffer overflow di stack dan heap, use-after-free, double-free, serta out-of-scope access. Dibandingkan dengan gdb yang bersifat reaktif (kita mencari bug setelah crash), ASan bersifat proaktif—program berhenti pada baris pertama yang melakukan akses tidak sah, lengkap dengan backtrace alokasi dan pembebasan terkait. Pada chapter ini kita akan mengaktifkan ASan dengan flag -fsanitize=address, menguji tiga kelas bug klasik (stack overflow, heap use-after-free, heap overflow), dan membaca output diagnostik yang dihasilkan. Dokumentasi resmi ASan tersedia di clang.llvm.org/docs/AddressSanitizer.html.
Mengaktifkan AddressSanitizer #
Cara mengaktifkan ASan sangat sederhana: tambahkan flag -fsanitize=address saat kompilasi dan linking. gcc dan clang keduanya mendukung flag ini. Tambahkan -g agar backtrace menyertakan nomor baris sumber, dan -O0 agar optimasi tidak mengaburkan urutan baris.
gcc -fsanitize=address -g -O0 -std=c11 program.c -o programASan menambahkan runtime library (libasan.so) yang di-link secara otomatis. Runtime ini menggantikan implementasi standar malloc/free dengan versi yang melacak setiap alokasi. Setiap byte memori yang dialokasikan dikelilingi redzone yang ditandai sebagai poisoned; akses ke redzone memicu laporan error.
Deteksi Stack Buffer Overflow #
Bug klasik pertama adalah akses ke elemen array di luar batas. Pada contoh berikut, array arr memiliki 5 elemen (indeks valid 0..4), tetapi program mencoba membaca arr[5].
/* File: /tmp/test_asan1.c */
#include <stdio.h>
int main(void) {
int arr[5] = {1, 2, 3, 4, 5};
int x = arr[5]; /* out-of-bounds: index valid 0..4 */
printf("x = %d\n", x);
return 0;
}gcc -fsanitize=address -g -O0 -std=c11 /tmp/test_asan1.c -o /tmp/test_asan1
/tmp/test_asan1Output yang Dihasilkan:
=================================================================
==9208==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f88f9800034 at pc 0x55e60c094399 bp 0x7ffd6c3674f0 sp 0x7ffd6c3674e8
READ of size 4 at 0x7f88f9800034 thread T0
#0 0x55e60c094398 in main /tmp/test_asan1.c:4
#1 0x7f88fba25ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: 5e5b51f4c39af9ac589408c1dc432ba390a273cc)
#2 0x7f88fba25d64 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d64)
#3 0x55e60c0940e0 in _start (/tmp/test_asan1+0x10e0)
Address 0x7f88f9800034 is located in stack of thread T0 at offset 52 in frame
#0 0x55e60c0941b8 in main /tmp/test_asan1.c:2
This frame has 1 object(s):
[32, 52) 'arr' (line 3) <== Memory access at offset 52 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/test_asan1.c:4 in main
Shadow bytes around the buggy address:
=>0x7f88f9800000: f1 f1 f1 f1 00 00[04]f3 f3 f3 f3 f3 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Stack left redzone: f1
Stack right redzone: f3
==9208==ABORTINGMari kita bedah elemen kunci pada laporan ASan di atas:
==9208==ERROR: AddressSanitizer: stack-buffer-overflow: nomor dalam==...==adalah PID proses. Jenis error adalahstack-buffer-overflow—akses di luar batas array yang ada di stack.READ of size 4 at 0x7f88f9800034 thread T0: akses tidak sah berupa READ sebesar 4 byte (satuint) pada alamat tertentu, dilakukan oleh threadT0(thread utama). ASan membedakan READ (membaca) dan WRITE (menulis).#0 0x55e60c094398 in main /tmp/test_asan1.c:4: backtrace menunjukkan baris 4 (int x = arr[5];) sebagai titik akses tidak sah. Alamat instruksi (0x55e60c094398) berada di dalam binary/tmp/test_asan1.[32, 52) 'arr' (line 3) <== Memory access at offset 52 overflows this variable: ASan mendeteksi bahwa arrayarrdideklarasikan pada baris 3 dan menempati offset 32 sampai 52 (eksklusif) dalam stack frame. Akses pada offset 52 adalah 1 byte pertama di luararr—tepat di stack right redzone.f1 f1 f1 f1 00 00[04]f3 f3 f3 f3 f3: shadow memory menunjukkan 8 byte application diwakili 1 byte shadow.f1= stack left redzone (poisoned),00= addressable (boleh diakses),04= partially addressable (4 byte pertama valid, sisanya poisoned),f3= stack right redzone (poisoned). Byte dalam kurung[04]adalah lokasi akses ilegal.
Deteksi Heap Use-After-Free #
Bug kedua adalah akses ke memori yang sudah dibebaskan dengan free. Tanpa ASan, akses seperti ini bisa berhasil (mengembalikan data lama) atau crash secara random—tergantung apakah allocator sudah memberikan memori tersebut ke alokasi lain.
/* File: /tmp/test_asan2.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *buf = (char *)malloc(16);
if (!buf) return 1;
buf[0] = 'X';
free(buf);
/* use-after-free */
printf("buf[0] = %c\n", buf[0]);
return 0;
}gcc -fsanitize=address -g -O0 -std=c11 /tmp/test_asan2.c -o /tmp/test_asan2
/tmp/test_asan2Output yang Dihasilkan:
=================================================================
==9215==ERROR: AddressSanitizer: heap-use-after-free on address 0x502000000010 at pc 0x55bbf015325b bp 0x7fff94ffe320 sp 0x7fff94ffe318
READ of size 1 at 0x502000000010 thread T0
#0 0x55bbf015325a in main /tmp/test_asan2.c:9
...
0x502000000010 is located 0 bytes inside of 16-byte region [0x502000000010,0x502000000020)
freed by thread T0 here:
#0 0x7f78ef1328f8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
#1 0x55bbf0153226 in main /tmp/test_asan2.c:7
previously allocated by thread T0 here:
#0 0x7f78ef133c57 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x55bbf01531ca in main /tmp/test_asan2.c:4
SUMMARY: AddressSanitizer: heap-use-after-free /tmp/test_asan2.c:9 in main
Shadow bytes around the buggy address:
=>0x502000000000: fa fa[fd]fd fa fa fa fa fa fa fa fa fa fa fa faMari kita uraikan tiga backtrace yang ASan tampilkan untuk use-after-free:
- Backtrace utama (
#0 ... in main /tmp/test_asan2.c:9): titik akses tidak sah. Baris 9 adalahprintf("buf[0] = %c\n", buf[0]);yang membacabuf[0]setelahfree. freed by thread T0 here: #0 ... in free ... #1 ... in main /tmp/test_asan2.c:7: ASan menampilkan kapan dan di mana memori tersebut dibebaskan. Baris 7 adalahfree(buf);. Backtracefreeberasal dari runtime ASan, bukan dari kode pengguna.previously allocated by thread T0 here: #0 ... in malloc ... #1 ... in main /tmp/test_asan2.c:4: ASan menampilkan kapan dan di mana memori tersebut dialokasikan. Baris 4 adalahchar *buf = (char *)malloc(16);. Informasi ini memungkinkan kita melacak seluruh siklus hidup blok memori.
Byte shadow fa fa[fd]fd menunjukkan fa = heap left redzone, fd = freed heap region (poisoned), dengan byte di dalam kurung [fd] adalah lokasi akses ilegal.
Deteksi Heap Buffer Overflow #
Bug ketiga adalah penulisan ke indeks yang berada tepat di luar alokasi heap. Pada contoh berikut, malloc(5 * sizeof(int)) mengalokasikan ruang untuk 5 int, tetapi loop menulis sampai indeks 5 (6 elemen).
/* File: /tmp/test_asan3.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = (int *)malloc(5 * sizeof(int));
if (!arr) return 1;
for (int i = 0; i <= 5; i++) { /* bug: <= seharusnya < */
arr[i] = i * 10;
}
printf("arr[5] = %d\n", arr[5]);
free(arr);
return 0;
}gcc -fsanitize=address -g -O0 -std=c11 /tmp/test_asan3.c -o /tmp/test_asan3
/tmp/test_asan3Output yang Dihasilkan:
=================================================================
==9222==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000000054 at pc 0x55d4e28e9244 bp 0x7ffc8d13e700 sp 0x7ffc8d13e6f8
WRITE of size 4 at 0x503000000054 thread T0
#0 0x55d4e28e9243 in main /tmp/test_asan3.c:7
...
0x503000000054 is located 0 bytes after 20-byte region [0x503000000040,0x503000000054)
allocated by thread T0 here:
#0 0x7fb71e5abc57 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x55d4e28e91ca in main /tmp/test_asan3.c:4
SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/test_asan3.c:7 in main
Shadow bytes around the buggy address:
=>0x503000000000: fa fa 00 00 00 fa fa fa 00 00[04]fa fa fa fa faBedah elemen kunci pada output heap-buffer-overflow:
WRITE of size 4 at 0x503000000054: akses tidak sah berupa WRITE sebesar 4 byte. ASan membedakan WRITE dari READ—WRITE biasanya lebih berbahaya karena dapat merusak data struktur internal allocator atau menyebabkan crash kemudian.#0 ... in main /tmp/test_asan3.c:7: baris 7 adalaharr[i] = i * 10;. Saati == 5, indeks tersebut di luar alokasi.0x503000000054 is located 0 bytes after 20-byte region [0x503000000040,0x503000000054): ASan mengonfirmasi bahwa alamat0x...54adalah tepat 0 byte setelah akhir region 20 byte (5 elemen * 4 byte). Region dimulai di0x...40dan berakhir di0x...54(eksklusif).Shadow bytes ... 00 00[04]fa: 2 byte shadow00 00menunjukkan 16 byte pertama addressable; byte04menunjukkan 4 byte terakhir addressable dari blok 20 byte; bytefasetelahnya menandai heap right redzone. Akses tepat pada byte04yang partially addressable—angka dalam kurung menandai lokasi error.
Variabel Lingkungan ASan #
ASan menyediakan beberapa variabel lingkungan untuk mengontrol perilaku runtime. Variabel ini diawali dengan ASAN_OPTIONS dan dipisahkan dengan titik dua.
| Variabel | Fungsi |
|---|---|
ASAN_OPTIONS=detect_leaks=1 | aktifkan LeakSanitizer (default: on di Linux) |
ASAN_OPTIONS=abort_on_error=1 | panggil abort() setelah laporan agar core dump dihasilkan |
ASAN_OPTIONS=halt_on_error=0 | lanjutkan eksekusi setelah error (mengumpulkan banyak error) |
ASAN_OPTIONS=print_stats=1 | cetak statistik memori ASan sebelum keluar |
ASAN_OPTIONS=quarantine_size_mb=256 | atur ukuran karantina use-after-free |
ASAN_OPTIONS=abort_on_error=1:print_stats=1 /tmp/test_asan1Output yang Dihasilkan:
=================================================================
==9208==ERROR: AddressSanitizer: stack-buffer-overflow on address ...
...
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/test_asan1.c:4 in main
...
==9208==ABORTINGBatasan ASan #
ASan sangat kuat tetapi punya batasan yang perlu dipahami:
- Overhead memori 2-3x: setiap 8 byte memori application dipetakan ke 1 byte shadow. Untuk program dengan alokasi besar, ini bisa menjadi masalah.
- Perlambatan 2x: instrumentasi setiap akses memori menambah overhead. ASan tidak cocok untuk profiling performa.
- Tidak mendeteksi data race: ASan tidak mendeteksi bug konkurensi; gunakan ThreadSanitizer (
-fsanitize=thread) untuk itu. - Tidak mendeteksi uninitialized read: gunasi MemorySanitizer (
-fsanitize=memory, hanya clang) untuk itu.