Buffer overflow adalah bug ketika program menulis melebihi batas buffer yang dialokasikan. C tidak melakukan bounds checking otomatis pada akses array—programmer harus memastikan setiap buf[i] berada dalam batas yang valid. Buffer overflow adalah salah satu bug klasik keamanan di C; banyak worm terkenal (Code Red, Slammer, Blaster) mengeksploitasi overflow di server. Untungnya, AddressSanitizer (ASan) dapat mendeteksi overflow baik di stack maupun heap secara akurat. Pada chapter ini kita akan membuat program dengan stack buffer overflow, lalu memverifikasi ASan menangkapnya.
Apa itu Buffer Overflow #
Buffer overflow terjadi ketika program menulis (atau membaca) melebihi batas buffer. Klasifikasinya:
- Stack buffer overflow — menulis melebihi array lokal, dapat menimpa return address di stack. Inilah vektor klasik serangan stack smashing.
- Heap buffer overflow — menulis melebihi blok heap, dapat merusak metadata allocator atau menimpa data alokasi tetangga.
- Off-by-one error — menulis tepat satu byte melebihi batas, sering akibat salah memilih
<vs<=atau lupa byte\0penutup string.
Penyebab umum buffer overflow:
strcpytanpa cek panjang —strcpy(dst, src)menyalin sampai\0disrc; bilasrclebih panjang daridst, terjadi overflow.gets— fungsi berbahaya yang membaca input tanpa batas, sudah dihapus di C11.- Loop dengan
<=—for (i = 0; i <= n; i++) buf[i] = ...;menulisn+1elemen padahalbufhanya berisin. - Aritmetika pointer salah —
p + ndi mananmelebihi ukuran blok yang dialokasikan.
Mekanisme kerja: di C, akses buf[i] diterjemahkan ke *(buf + i)—CPU hanya menghitung alamat dan mengaksesnya. Tidak ada pemeriksaan apakah alamat itu berada dalam buffer yang valid. Inilah alasan overflow sering tidak terdeteksi dalam testing singkat—data yang ditimpa mungkin tidak terpakai saat itu, dan bug baru muncul kemudian.
Demo: Stack Buffer Overflow #
Kita buat buffer 8 byte dan mencoba menulis 16 byte. Tanpa ASan, program mungkin berjalan “normal” atau crash secara misterius; dengan ASan, kita mendapatkan laporan tepat.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[8] = "hello"; /* buffer 8 byte, isi "hello\0" */
printf("Sebelum overflow: buf = \"%s\", alamat = %p\n", buf, (void*)buf);
/* BUG: menulis 16 byte ke buffer 8 byte -> buffer overflow */
for (int i = 0; i < 16; i++) {
buf[i] = 'A';
}
printf("Setelah overflow: buf = \"%s\"\n", buf);
return 0;
}Kompilasi dengan ASan dan jalankan:
gcc -std=c11 -Wall -Wextra -fsanitize=address,undefined \
-o /tmp/test_overflow /home/z/my-project/work/tests/test_overflow.c
/tmp/test_overflowOutput yang Dihasilkan:
test_overflow.c:13:12: runtime error: index 8 out of bounds for type 'char [8]'
test_overflow.c:13:16: runtime error: store to address 0x7f9186500028 with insufficient space for an object of type 'char'
=================================================================
==24610==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f9186500028 at pc 0x564c9bdef396 bp 0x7ffc056caba0 sp 0x7ffc056cab98
WRITE of size 1 at 0x7f9186500028 thread T0
#0 0x564c9bdef395 in main (/tmp/test_overflow+0x1395)
#1 0x7f9188ad4ca7 in (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7)
#2 0x7f9188ad4d64 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d64)
Address 0x7f9186500028 is located in stack of thread T0 at offset 40 in frame
#0 0x564c9bdef1f8 in main (/tmp/test_overflow+0x11f8)
This frame has 1 object(s):
[32, 40) 'buf' (line 6) <== Memory access at offset 40 overflows this variable
SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/test_overflow+0x1395) in mainASan mendeteksi WRITE of size 1 ke alamat 0x...028 yang berada di luar buffer buf (yang berada di offset 32-39 dalam stack frame). UBSan (yang terintegrasi dengan flag -fsanitize=undefined) juga melaporkan “index 8 out of bounds for type ‘char [8]’"—deteksi tingkat bahasa C yang lebih dini.
Mari kita telaah baris-baris kode di atas:
- Baris 6 (
char buf[8] = "hello";): deklarasi buffer 8 byte di stack. String literal"hello"(5 huruf +\0= 6 byte) muat di buffer; sisanya 2 byte otomatis diisi\0. Inisialisasi ini sah. - Baris 7 (
printf("Sebelum overflow: buf = \"%s\", alamat = %p\n", buf, (void*)buf);): mencetak isi dan alamatbuf. Operasi aman karenabufmasih dalam batas yang valid. - Baris 11 (
for (int i = 0; i < 16; i++)): loop iterasi dari 0 hingga 15. Bufferbufhanya berisi 8 elemen (indeks 0-7), sehingga aksesbuf[8]hinggabuf[15]jelas overflow. - Baris 12 (
buf[i] = 'A';): penulisan karakter'A'kebuf[i]. Saati = 8, ASan menghentikan program karena akses ke alamat di luarbuf. - Baris 14 (
printf("Setelah overflow: buf = \"%s\"\n", buf);): tidak pernah dieksekusi karena ASan membatalkan program di baris 12 saati = 8.
Mencegah Buffer Overflow #
Strategi pencegahan:
- Gunakan
strncpy,snprintf,strlcpyalih-alihstrcpy,sprintf. Fungsi-fungsi ini menerima parameter ukuran dan tidak akan menulis melebihi batas. - Validasi input — selalu periksa panjang input sebelum menyalin ke buffer.
- Gunakan
fgets(buf, sizeof(buf), fp)alih-alihgets(buf)—fgetsmenerima parameter ukuran. - Compile dengan
-D_FORTIFY_SOURCE=2— gcc/glibc menyediakan hardening yang menggantikanstrcpy/memcpydengan versi yang memeriksa batas. - Stack protector (
-fstack-protector-strong) — gcc menyisipkan canary di antara buffer dan return address; bila canary berubah, program membatalkan diri sendiri. - Gunakan ASan di CI — testing rutin dengan ASan menangkap overflow yang tidak terlihat.
Pencegahan: strncpy sebagai Pengganti strcpy #
Sebagai kontras, kita tunjukkan cara menyalin string secara aman dengan strncpy yang menerima parameter ukuran. Buffer tujuan tetap berukuran 8 byte, tetapi strncpy tidak akan menulis melebihi batas.
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[8];
const char *src = "Belajar C"; /* 9 char + '\0' = 10 byte */
/* strncpy: menyalin maksimal sizeof(buf)-1 byte, sisanya '\0' */
strncpy(buf, src, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0'; /* pastikan null-terminated */
printf("src: \"%s\" (panjang %zu)\n", src, strlen(src));
printf("buf: \"%s\" (panjang %zu, ukuran buffer %zu)\n",
buf, strlen(buf), sizeof(buf));
return 0;
}Output yang Dihasilkan:
src: "Belajar C" (panjang 9)
buf: "Belajar" (panjang 7, ukuran buffer 8)String "Belajar C" (9 karakter) tidak muat di buffer 8 byte. strncpy menyalin maksimal sizeof(buf) - 1 = 7 karakter (yaitu "Belajar") dan menghentikan penyalinan—tidak ada overflow. Baris buf[sizeof(buf) - 1] = '\0' memastikan string selalu null-terminated bahkan bila strncpy memotong. Inilah cara menghindari buffer overflow di C: selalu sertakan parameter ukuran dan hormati batas tersebut.