Skip to content

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
$ clang -fsanitize=address -fsanitize-address-use-after-scope uas.c -o uas
$ ./uas
==7777==ERROR: AddressSanitizer: stack-use-after-scope on address 0x...
    #0 0x... in main uas.c:9
  Address ... is located in stack of thread T0 ...

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.

References