Skip to content

FORTIFY_SOURCE

Sınırsız libc çağrılarını, tespit edilen overflow'da abort eden bounds-aware __*_chk varyantlarına yeniden yazan glibc+compiler hardening (-D_FORTIFY_SOURCE=1/2/3).

Mechanism

Note

Flag set edilip optimization açıkken compiler, memcpy/strcpy/sprintf gibi çağrıları __memcpy_chk/__strcpy_chk/__sprintf_chk ile değiştirir ve fazladan bir argüman geçirir: __builtin_object_size(dest, type)'tan gelen, compiler'ın hedef object boyutu tahmini. _chk fonksiyonu istenen uzunluğu bilinen boyutla karşılaştırır ve overrun durumunda __chk_fail() çağırır (ki o da abort() eder). Check, compiler'ın object'e dair görüşüne dayandığı için boyutun çözülebilmesi adına çağrının tipik olarak inline edilebilir olması gerekir ve optimization (-O1+) şarttır — -O0'da hiç fortification yoktur.

Level 3 (GCC 12+, Clang) yerine __builtin_dynamic_object_size koyar ve runtime bir boyut ifadesi üretir; böylece heap/dinamik boyutlu buffer'lar da check edilebilir.

Walkthrough

gcc -O2 -D_FORTIFY_SOURCE=2 overflow.c -o overflow

char a[3]; strcpy(a, longstring); için compiler __strcpy_chk(a, longstring, 3) üretir; runtime'da bir overrun abort eder:

*** buffer overflow detected ***: terminated
Aborted (core dumped)

Level farkları:

  • =1, __builtin_object_size(a, 0) kullanır — gevşek, tüm-allocation görüşü.
  • =2, daha katı olan __builtin_object_size(a, 1) kullanır: strcpy(&g.b.a[1], b) için level 1 boyut 11 (tam struct) verirken level 2 3 (yalnızca en içteki field) verir. =2 ayrıca yazılabilir bir format string içindeki %n'i de işaretler: *** %n in writable segment detected ***.
  • =3, __builtin_dynamic_object_size kullanır, böylece malloc(n) buffer'ları runtime'da check edilebilir.

Detection

Gözlemlenebilir iz, *** buffer overflow detected *** (veya *** %n in writable segment detected ***) mesajıyla gelen runtime abort()'tur.

Mitigation

Warning

Kapsam, object boyutunun compiler tarafından bilindiği durumlarla sınırlıdır. Boyut opak ise — =1/=2'de birçok heap pointer, inline edilmemiş sınırlar boyunca geçen pointer'lar veya optimizer'ın fold edemediği boyutlar — __builtin_object_size, (size_t)-1 döner ve çağrı check edilmeyen libc fonksiyonuna düşer, yani overflow yakalanmaz.

Yalnızca glibc'nin _chk wrapper'ı sağladığı libc fonksiyonlarını fortify eder; doğrudan buffer write'ları, özel copy loop'ları ve read()-into-buffer kapsam dışıdır. Kernel karşılığı için FORTIFY_SOURCE against canary leaks ve hardened usercopy'ye bakın.

References