Use-after-scope¶
Bir local'in adresini, onu tanımlayan lexical block bittikten sonra kullanmak; function henüz return etmemiş olsa bile o variable'ın storage'ı aynı frame'deki diğer local'ler tarafından geri kazanılabilir.
Mechanism¶
C/C++'ta bir local'in lifetime'ı, function return ettiğinde değil, onu tanımlayan block'un kapanış parantezinde sona erer. Compiler aynı function'ın ilerleyen kısmında o stack slot'unu başka bir variable için yeniden kullanmakta serbesttir. Bu yüzden inner-scope bir variable'ın adresini yakalamış bir pointer, kontrol block'tan çıkar çıkmaz dangling hale gelir ve frame hâlâ canlıyken kardeş bir variable onun altındaki byte'ları ezebilir.
int *p;
{
int x = 7;
p = &x; /* p captures x's slot */
} /* x's lifetime ends here; slot now reusable */
{
int y = 0x4141; /* compiler may place y at x's old slot */
(void)y;
}
use(*p); /* reads y's bytes, not 7 -- use-after-scope */
Note
Bu, use-after-return'den farklıdır: function hâlâ çalışmaktadır, yani frame
mevcuttur, ama o belirli slot sonraki bir block için geri dönüştürülmüştür.
Adresi dış bir container'a saklanan loop-body geçici değişkenlerinde ve bir
if/for block'unun içindeki bir variable'ın &'ini almakta sıkça karşılaşılan
bir footgun'dır.
Walkthrough¶
Yaygın gerçek bir pattern — loop-scope'lu bir geçici değişkenin adresini yakalamak:
const char *names[3];
for (int i = 0; i < 3; i++) {
char tmp[8];
snprintf(tmp, sizeof tmp, "n%d", i);
names[i] = tmp; /* BUG: tmp's scope ends each iteration */
}
/* all names[] now point at the same recycled slot -> garbage */
puts(names[0]);
ASan bunu -fsanitize-address-use-after-scope ile derlendiğinde yakalar:
compiler her scope sınırında poison/unpoison marker'ları üretir, böylece block'u
bitmiş bir slot'a yapılan bir erişim trap'lenir.
Derleme ve raporu gözlemleme
Detection¶
-fsanitize-address-use-after-scope ile ASan ona özel detector'dür; yakın
Clang sürümlerinde ASan build'lerinde -O0'da varsayılan olarak açıktır.
-Wdangling-pointer (GCC) bazı biçimleri statik olarak yakalar. MSan/Valgrind,
ortaya çıkan uninitialized/garbage okumalarını dolaylı olarak yüzeye çıkarabilir.
Mitigation¶
Block-scope'lu bir local'in adresini block'unun ötesinde saklama; variable'ın scope'unu kullanımına denk gelecek şekilde genişlet ya da daha uzun ömürlü bir storage'a value ile kopyala. Test/CI build'lerinde ASan use-after-scope'u açık tut.