Skip to content

NULL pointer dereference

Geçerli olması beklenen ama NULL'a eşit bir pointer'ı dereference etmek, normalde process'i crash eder — ve adres 0 map'lenebilir olduğunda code execution'a escalate olur.

Mechanism

Bir NULL pointer dereference'i (CWE-476), kod geçerli olmasını beklediği ama NULL (sayısal olarak adres 0) tutan bir pointer üzerinden okuduğunda ya da yazdığında olur. Doğru yapılandırılmış bir sistemde adres 0'da hiçbir object yaşamadığı için, erişim fault verir — userland'de bir SIGSEGV, kernel'de bir kernel oops/BSOD.

Note

Kırılan invariant "bu pointer kullanımdan önce doğrulandı"dır. CWE-476'ya göre zafiyet, pointer'ların kullanımdan önce doğru başlatıldığından emin olunmamasından doğar; tipik olarak yetersiz error handling, eksik return-value kontrolleri ya da kontrolsüz harici input nedeniyle. Standart sonuç availability kaybıdır: "NULL pointer dereference'leri, exception handling ... mevcut ve uygulanmış olmadıkça genellikle process'in başarısızlığıyla sonuçlanır." NULL'ın bellek adresi 0x0'a map'lendiği ve ayrıcalıklı kodun ona erişebildiği nadir durumda, "belleği okumak ya da yazmak mümkün hale gelir, potansiyel olarak code execution'a olanak tanır" — ki kernel NULL-deref sınıfının bu kadar tehlikeli hale gelmesinin tam sebebi budur (bkz. null-page-mapping).

Walkthrough

Ders kitabı bug'ı: NULL döndürebilen ve sonucu bir kontrol olmadan kullanılan bir function.

#include <netdb.h>
#include <string.h>

void resolve(struct in_addr addr) {
    struct hostent *h = gethostbyaddr(&addr, sizeof(addr), AF_INET);
    // gethostbyaddr() returns NULL on failure (e.g. DNS down)
    char buf[64];
    strcpy(buf, h->h_name);   // NULL deref: h may be NULL
}

Çözülemeyen bir adrese karşı derleyip çalıştırmak fault verir:

$ cc -g null.c -o null && ./null
Segmentation fault (core dumped)

Minimal reproducer, adres 0'ın okunmasını açık hale getirir:

int main(void) {
    int *p = NULL;
    return *p;          // read at virtual address 0 -> SIGSEGV
}
Fault'u bir debugger altında incelemek
$ gdb -q ./null
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x... in main () at null.c:3
3       return *p;
(gdb) p p
$1 = (int *) 0x0

Warning

Bir NULL deref'i catch-all bir signal handler ya da try/except içine sararak "düzeltme" — bu, mantık bug'ını gizler ve programı tutarsız bir state'te bırakabilir. Eksik validation'ı düzelt. Kernel'de, zero page etkilenebildiğinde (mmap_min_addr=0, bir LSM açığı ya da NULL'dan controllable bir offset) bir NULL deref yalnızca bir crash değil, bir güvenlik bug'ıdır.

Detection

  • Static analysis, muhtemelen-NULL bir kaynağın bir dereference sink'ine ulaştığı data-flow path'lerini işaretler (CWE-476 yüksek-sinyalli bir static-analysis hedefidir).
  • Dynamic analysis: AddressSanitizer ve fuzzing, handle edilmemiş NULL koşullarını tetikler; robustness/fault-injection testi (düşük bellek, eksik DNS) kontrolsüz return value'ları açığa çıkarır.
  • Crash triage: adres 0x0'da ya da yakınında (ya da obj == NULL iken obj->field'den gelen küçük bir offset'te) fault veren bir SIGSEGV.

Mitigation

  • Dereference'ten önce pointer'ları NULL için kontrol et, özellikle NULL döndürdüğü belgelenen function'ların sonuçlarını (malloc, gethostbyaddr, lookup'lar).
  • Tüm function return value'larını doğrula; pointer değişkenlerini açıkça başlat.
  • Pointer değerlerini etkileyen harici veriye input validation uygula.
  • Defense in depth: vm.mmap_min_addr'i sıfırdan farklı tut ve SMEP/SMAP'ı etkinleştir, böylece bir kernel NULL deref'i map'lenmiş bir zero page aracılığıyla code execution'a çevrilemesin.

References