GCC-Memory Error Detection(Bellek Hatası Algılama) Part1

Kerim Fırat
4 min readJul 12, 2021

GCC,birçok programlama hatasını tespit etmeye yardımcı olmak için tasarlanmış zengin bir dizi özelliğe sahiptir. Çalışan bir programın belleğinin bozulması ve bu bozulmalarının bazı durumlarda programı güvenlik tehditlerine karşı savunmasız hale getirmesi ciddi ve üzerinde durulması gereken önemli konulardır. GCC 2006'dan beri C ve Cpp programlarında arabellek taşmalarının bir alt kümesini tespit etmek ve önlemek için etkili bir çözüm sağlamıştır.Bu çözüm,derleyici teknolojisine dayanmasına rağmen,eşzamanlı GNU C kitaplığı makrosunda türetilen “Fortify Source” aldı altında bilinir:”_FORTIFY_SOURCE”. GCC,2006'daki 4.1 sürümünden bu yana ve bu tür hataları tespit etme yeteneği ile önemli ölçüde değişti ve gelişti. Özellikle GCC 7,bu alanda birkaç yeni programlama hatası türünün tespit edilmesine yardımcı olan bir dizi geliştirme içerir. Bu makale,bu yeni özelliklere kısa bir genel bakış sağlar.

Önemli Not

Bu makalede tartışılan seçenekler hem optimizasyonlu hem de optimizayonsuz olarak mevcuttur. Optimizasyon ile kullanıldıklarında,sabit olmayan fonksiyon argüman değerlerinden bazı maksimum değerlerin üzerinde olmasından bile kaynaklanan sorunlar ortaya çıkarabilirler. Bu optimizasyonlar bu tür bazı kusurları keşfedebilse de,hepsini bulamıyorlar.Bu seçenekler etkinleştirildiğinde ve hiçbir uyarı olmadan başarılı olarak derlendiğinde,derlenen programda hata olmadığının garantisi olmadığını ve kapsamlı testlerin yerini tutamayacağını akılda tutmak önemlidir.

Seçenek: “-Walloc-size-larger-than”

“-Walloc-size-larger-than” seçeneği “size(boyut)” olarak bir parametre alır.
Bu parametre ile boyutu aşan bellek ayırma fonksiyonlarına yapılan çağrıları algılar. Tam kullanımı aşağıdaki gibidir:

-Walloc-size-larger-than=size

Aşırı boyutta çağrıların başarılı olma olasılığı düşük olduğundan(hiçbir nesne “PTRDIFF_MAX” bayttan büyük olamaz),bunlar genellikle programlama hatalarının sonucudur. Bu seçenek yalnızca “malloc” gibi standart bellek ayırma işlevleri için değil,aynı zamanda “alloc_size” attribute ile dekore edilmiş kullanıcı tanımlı fonksiyonlar için de çalışır.Attribute,GCC’ye bir fonksiyonun boyutu,argümanı veya argümanların bir ürünü tarafından verilen belleği döndürdüğünü söyler.

-Walloc-size-larger-than=PTRDIFF_MAX” seçeneği “-Wall”’a dahildir.

Örneğin aşağıdaki “malloc” çağrısı hatalı bir şekilde fonksiyona negatif bir argüman iletmekten kaçınmaya çalışıyor. Çünkü fonksiyonun argümanı “size_t” türüne dönüştürüldükten sonra,negatif bir değer “PTRDIFF_MAX” maksimum değerini aşan bir değerle sonuçlanır ve bu kullanım-çağrı teşhis ediliyor.

void* f (int n)
{
return malloc (n > 0 ? 0 : n);
}
warning: argument 1 range [2147483648, 4294967295] exceeds maximum object size 2147483647 [- Walloc-size-larger-than=]

Seçenek: “-Walloc-zero”

-Walloc-zero” seçeneği,standart çağrıların yanısıra kullanıcı tanımlı bellek ayırma işlevlerinin “alloc_size” attribute ile dekore edilmiş sıfır(0) argümanıyla yapılan çağrıları da algılar. Bu seçeneğe “-Wall” veya “-Wextra” dahil değildir.Bu yüzden açıkça belirtilerek etkinleştirilmelidir.

Seçenek: “-Walloca”

alloca” fonksiyonu parametre olarak verilen boyut miktarı kadar stack’da alan ayırır ve ayrılan alanı gösteren bir pointer döndürür. Bu fonksiyon,parametre olarak belirtilen miktarda bellek alanı olup olmadığnı kontrol etmez. Bu nedenle dikkatli kullanılmadığında stack’ı kolayca tüketebilir. Çünkü bu fonksiyon yardımıyla ayrılan belleğin tekrar serbest bırakılabilmesi için,fonksiyonun yer aldığı kod bloğunun sonuna gelinmiş olması gerekir.
Eğer bu şart sağlanmaz ise,ayrılan bellek serbest bırakılmaz.
Örneğin “x86_64”’te bir linux prossesin default stack boyutu 8 MB’dir.
Bu değer 32 bit Linux sisteminde ise 2 MB’dir. Bu fonksiyonu kullanırken özellikle dikkatli olmak gerekir.Fonksiyonun kullanımıyla birlikte öngürülemeyen hatalar-bug’lar meydana çıkabilir ve çıkmıştır.
Dolayısıyla fonksiyon tehlikeli olarak kabul edilir ve kullanımı genellikle önerilmez. “-Walloca” seçeneği ile bir programda çağrılan-tanımlanan “alloca” fonksiyonları tespit edilebilir.Bu seçeneğe “-Wall” veya “-Wextra” dahil değildir.Bu yüzden açıkça belirtilerek etkinleştirilmelidir.

Örneğin aşağıdaki kod örneğinde bir döngü içinde “alloca” fonksiyonu kullanılıyor. Bu tür kullanım tehlikelidir.Burada ayrılan bellek döngü her yineleme süresi boyunca gerekli olsa da,ayrılan bellek fonksiyon çıkışına kadar serbest bırakılmaz. Bu kullanımda stack alanı “n” değişkeninin insafına bırakılmıştır.Çünkü döngü(i) arttıkça talep edilen boyut büyüyecek ve “n”(döngü sayısı) kadar devam edecektir. “-Walloca” seçeneğiyle “alloca” kullanımı aşağıdaki gibi tespit ediliyor.

void f (int n)
{
for (int i = 0; i < n; ++i)
{
void *p = alloca (i);

}
}
warning: unbounded use of 'alloca' [-Walloca]

Seçenek: “-Walloca-larger-than”

-Walloca-larger-than=size” seçeneği,”-Walloca”’dan daha fazla sebesttir-izinlidir. Çünkü bu yalnızca argümanı belirtilen boyutu aşabilen veya bu boyutu aşmamak için yeterince kısıtlı olduğu bilinmeyen “alloca” fonksiyonuna yapılan çağrıları algılar. Diğer çağrılar güvenli kabul edilir ve göz ardı edilir. Dolayısıyla göz ardı edilen çağrılar teşhis-tespit edilmez.
Bu seçeneğe “-Wall” veya “-Wextra” dahil değildir.Bu yüzden açıkça belirtilerek etkinleştirilmelidir.

Örneğin aşağıda verilen kod parçasını “-Walloca-larger-than=1024” ile derlemek bir uyarıyla sonuçlanacaktır. Çünkü kodda “n” ile sağlanan değer 1 KB veya daha küçük boyutlarda “alloca”’yı çağırıyor gibi görünse de,n ile negatif bir değer,işlevin sınırın çok üzerinde bir çağrıyla sonuçlanmasına neden olur. Dolayısıyla bu kullanım teşhis-tespit edilecek ve aşağıdaki uyarıyı çıktı olarak verecektir.

void f (int n)
{
char *d;
if (n < 1025)
d = alloca (n);
else
d = malloc (n);

}
warning: argument to 'alloca may be too large due to conversion from 'int' to 'long unsigned int' [-Walloca-larger-than=]

Bunun tersine,aşağıda verilen kod parçasında olduğu gibi hiçbir şekilde sınırlandırılmamış bir “alloca” çağrısı,seçeneğin boyut argümanından bağımsız olarak aşağıdaki uyarıyı çıktı olarak verecektir.
Çünkü bu kullanımda yine “n” değişkeni öngürülemez bir değerle gelebilir. Dolayısıyla bu değer eksi bir değer olsa da,tam tersi üst limit olsa da sınır aşımı olacaktır.

void f (size_t n)
{
char *d = alloca (n);

}
warning: unbounded use of 'alloca' [-Walloca-larger-than=]

KAYNAKLAR:
https://gcc.gnu.org/
https://man7.org/linux/man-pages/man3/alloca.3.html
https://developers.redhat.com/blog/2017/02/22/memory-error-detection-using-gcc
https://www.gnu.org/software/libc/
https://access.redhat.com/blogs/766093/posts/1976213
https://gcc.gnu.org/gcc-7/changes.html

-end of

--

--

Kerim Fırat
Kerim Fırat

Written by Kerim Fırat

Senior Android Platform(AOSP,AAOS) Architect,Open Source Contributor | Turkey Java User Group Vice Chairman | Author

No responses yet