AddressSanitizer (ASan) adalah sanitizer paling sering menyelamatkan program C++: ia mengawasi setiap byte memori yang dialokasikan dan setiap akses terhadapnya, lalu berhenti pada pelanggaran pertama dengan laporan yang menunjuk baris pelaku dan baris alokasi. Error memori seperti ini adalah spesialisasi bahasa C++ yang tidak enak — tanpa ASan, akses melampaui batas bisa diam bertahun-tahun lalu “kadang-kadang” membuat program runtuh di mesin pengguna, jauh dari tangan Anda. Chapter ini menjalankan tiga program bermasalah di bawah ASan, membaca laporannya satu per satu, lalu menutup dengan versi amannya.
Mengaktifkan ASan #
Satu flag ditambah satu flag informasi debug — itulah seluruh pengaturannya. Flag -g tidak wajib bagi ASan, tetapi tanpanya laporan kehilangan nama berkas dan nomor baris:
g++ -std=c++23 -g -Wall -Wextra -fsanitize=address program.cpp -o program
./programDi balik layar, ASan bekerja dengan dua sisi: compiler menyisipkan pemeriksa pada setiap akses memori, dan runtime mengelola “peta racun” (shadow memory) yang menandai byte mana yang sah, yang menjadi redzone di sekeliling setiap alokasi, dan yang sudah dilepas. Akses ke redzone — tanda buffer overflow — atau ke area yang sudah dilepas langsung tertangkap pada instruksinya, bukan belakangan saat data lain dirusak.
Kasus 1: Heap-Buffer-Overflow #
Program ini membaca data[3] padahal array hanya berisi tiga elemen (indeks sah: 0, 1, 2):
#include <iostream>
int main() {
int* data = new int[3]{10, 20, 30};
std::cout << data[3] << "\n"; // melewati batas: indeks 3 tidak ada
delete[] data;
return 0;
}Tanpa ASan, program ini kemungkinan besar mencetak angka sampah lalu lanjut normal — bug yang paling licin. Dengan ASan, eksekusi terhenti dan stderr menampilkan laporan seperti berikut (hasil nyata di sandbox penulis, disingkat; alamat dan path akan berbeda di mesin Anda):
=================================================================
==30299==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50200000001c at pc 0x564c95c862fd bp 0x7ffe3d56e4b0 sp 0x7ffe3d56e4a8
READ of size 4 at 0x50200000001c thread T0
#0 0x564c95c862fc in main demo-overflow.cpp:5
#1 0x7f7365492ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
0x50200000001c is located 0 bytes after 12-byte region [0x502000000010,0x50200000001c)
allocated by thread T0 here:
#0 0x7f7365ad78d8 in operator new[](unsigned long)
#1 0x564c95c861da in main demo-overflow.cpp:4
SUMMARY: AddressSanitizer: heap-buffer-overflow demo-overflow.cpp:5 in mainLaporan ini membaca dari atas ke bawah sebagai satu cerita:
- Jenis pelanggaran:
heap-buffer-overflow— akses keluar wilayah heap.READ of size 4berarti pembacaan 4 byte (ukuran satuint). - Frame
#0: lokasi aksesnya,demo-overflow.cpp:5dimain— persis barisdata[3]. - Kalimat lokasi:
0 bytes after 12-byte region— alamat yang diakses berada tepat setelah blok 12 byte (3 × 4 byte). Kata “0 bytes after” memberi tahu kita meleset persis satu elemen. - Blok
allocated by: alokasi bersangkutan dibuat di baris 4 — jadi kita tahu array mana yang dilampaui, bukan sekadar ada pelanggaran di udara. SUMMARY: ringkasan satu baris yang biasanya dikutip di issue atau log CI.
Kasus 2: Use-After-Free #
Bug berikutnya menulis ke blok yang sudah dilepas — pola yang dengan atau tanpa ASan sering “berjalan” di mesin developer lalu runtuh acak di produksi:
#include <iostream>
int main() {
int* p = new int[5]{1, 2, 3, 4, 5};
delete[] p;
p[0] = 99; // use-after-free: menulis ke memori yang sudah dilepas
std::cout << p[0] << "\n";
return 0;
}=================================================================
==30470==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000000040 at pc 0x557b1b4d0382 bp 0x7ffeb7f3cd30 sp 0x7ffeb7f3cd28
WRITE of size 4 at 0x503000000040 thread T0
#0 0x557b1b4d0381 in main demo-uaf.cpp:6
#1 0x7f2a992c1ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
0x503000000040 is located 0 bytes inside of 20-byte region [0x503000000040,0x503000000054)
freed by thread T0 here:
#0 0x7f2a999072e8 in operator delete[](void*)
#1 0x557b1b4d034a in main demo-uaf.cpp:5
previously allocated by thread T0 here:
#0 0x7f2a999068d8 in operator new[](unsigned long)
#1 0x557b1b4d01ca in main demo-uaf.cpp:4
SUMMARY: AddressSanitizer: heap-use-after-free demo-uaf.cpp:6 in mainKini laporan memuat tiga timeline dalam satu tampilan: akses ilegal di baris 6, pelepasan di baris 5, dan alokasi di baris 4. Urutan membacanya sama seperti membaca riwayat objek: lahir (allocated), dilepas (freed), lalu disentuh lagi (WRITE). Tiga baris itu biasanya cukup untuk memperbaiki bug dalam hitungan menit — biasanya dengan memindahkan penggunaan sebelum delete, atau lebih baik lagi, mengganti manajemen manual dengan smart pointer.
Kasus 3: Double-Free #
Melepas blok yang sama dua kali merusak pembukuan alokator dan bisa mengacaukan heap untuk seluruh proses:
#include <iostream>
int main() {
int* p = new int[5]{1, 2, 3, 4, 5};
delete[] p;
delete[] p; // double-free: blok yang sama dilepas dua kali
std::cout << "selesai\n";
return 0;
}=================================================================
==30487==ERROR: AddressSanitizer: attempting double-free on 0x503000000040 in thread T0:
#0 0x7fadeb51b2e8 in operator delete[](void*)
#1 0x5610aeead34d in main demo-doublefree.cpp:6
#2 0x7fadeaed5ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
0x503000000040 is located 0 bytes inside of 20-byte region [0x503000000040,0x503000000054)
freed by thread T0 here:
#0 0x7fadeb51b2e8 in operator delete[](void*)
#1 0x5610aeead33a in main demo-doublefree.cpp:5
SUMMARY: AddressSanitizer: double-free demo-doublefree.cpp:6 in mainPerhatikan dua frame main yang berdampingan: pelepasan pertama di baris 5, pelepasan kedua di baris 6. Pola pencegahannya konsisten sepanjang course ini: satu pemilik, satu pelepasan — dan bila kepemilikan tidak jelas, beralihlah ke std::unique_ptr yang otomatis melepas tepat satu kali.
Protokol Membaca Laporan ASan #
Kapan pun laporan panjang menyambut, jalani protokol lima langkah ini:
- Baris
ERROR:— kenali jenis pelanggaran dan alamatnya (overflow, use-after-free, double-free, stack-buffer-overflow, global-buffer-overflow). - Baris
READ/WRITE+ frame#0— ini kejadian: ukuran akses dan baris kode tempat pelanggaran terjadi. - Blok
freed by/previously allocated by— ini latar belakang: riwayat blok memori yang disentuh. - Kalimat
is located ...— hubungan posisi: tepat setelah blok (0 bytes after), di dalam blok (0 bytes inside), jarak dari kiri, dan sebagainya. SUMMARY— satu baris untuk dicatat di issue: jenis + berkas:baris + function.
Versi Aman: Biarkan Container yang Menjaga #
Program aman yang setara menunjukkan dua pertahanan: ukuran diambil dari container (tidak ada indeks meleset) dan akses berisiko dipagari .at() yang melempar exception bila di luar batas:
// demo-aman.cpp — operasi heap yang sama, tetapi dijaga batasnya.
#include <iostream>
#include <stdexcept>
#include <vector>
int main() {
std::vector<int> data = {10, 20, 30};
for (std::size_t i = 0; i < data.size(); ++i) {
std::cout << data[i] << "\n";
}
try {
std::cout << data.at(3) << "\n";
} catch (const std::out_of_range& e) {
std::cout << "tertangkap: " << e.what() << "\n";
}
return 0;
}Output yang Dihasilkan:
10
20
30
tertangkap: vector::_M_range_check: __n (which is 3) >= this->size() (which is 3)Tiga hal yang berubah dibanding versi bermasalah: alokasi dan pelepasan manual hilang total karena std::vector mengelola heap lewat RAII; loop membaca batas langsung dari data.size() sehingga indeks mustahil meleset; dan akses “berbahaya” yang ditulis sengaja untuk uji memakai .at(), yang memeriksa batas lalu melempar std::out_of_range — exception tertangkap rapi di catch, dan pesannya (milik libstdc++ di mesin ini) menyebut indeks 3 terhadap ukuran 3. Lari program ini di bawah ASan menghasilkan output yang sama persis tanpa satu pun laporan — kontrak “diam berarti bersih” yang kita bahas pada chapter sebelumnya.
ASan menutup kelas error memori, tetapi masih ada kelas bug yang memori-nya sah sementara arti angkanya yang tidak teratur: luapan bilangan bulat, shift melebihi lebar tipe, dan pointer yang tidak sejajar. Chapter berikutnya menghadirkan UBSan untuk itu.
Referensi: Clang — AddressSanitizer , Google sanitizers , GCC — Instrumentation Options .