GCC 14’te statik analiz iyileştirmeleri
(developers.redhat.com)- GCC 14’ün
-fanalyzerseçeneği, C kodunun birden çok yürütme yolunu sembolik yürütme ile izleyerek, çalışma zamanından önce yakalanması zor kusurları derleme aşamasında bulmaya odaklanır - Bu iyileştirmeler sonsuz döngü tespiti, buffer overflow görselleştirmesi, C string izleme ve taint analizinin varsayılan olarak etkinleştirilmesi başlıklarına ayrılıyor; odak noktası tanı çıktılarının daha anlaşılır hale getirilmesi
-Wanalyzer-out-of-bounds, basit bir uyarının ötesine geçerek buffer ile yazma konumu arasındaki ilişkiyi metin diyagramları ile gösteriyor ve UTF-8 stringlerde hangi karakterin ortasında taşma olduğunu da belirtiyor- Yeni
null_terminated_string_arg(PARAM_IDX)özniteliği, NUL sonlandırılmış string bekleyen API koşullarını analizöre ve kodu okuyanlara bildirerek NUL ile sonlandırılmamış buffer aktarım yollarını daha görünür hale getiriyor - Taint analizi,
-fanalyzerseçildiğinde varsayılan olarak açılıyor; böylece saldırgan kontrollü değerlerin üst sınır kontrolü olmadan boyut, indeks, ofset gibi alanlarda kullanıldığı akışlar daha kolay görülebiliyor
-fanalyzerın ele aldığı problem kapsamı
-fanalyzer, GCC’nin statik analiz geçişidir; C kaynak kodunun birden çok yürütme yolunu sembolik yürütme ile takip ederek derleme zamanında kusur bulmayı amaçlar- GCC 14’teki iyileştirmeler, 2024 Nisan’daki resmî sürüm öncesinde derlendi; yazının kaleme alındığı sırada GCC 14.1 sürümünün 2024 Nisan içinde gelmesi bekleniyordu
- GCC 14.0 prerelease sürümü Fedora 40 Beta içinde zaten kullanılıyordu
- Örnekler, Compiler Explorer üzerinde yeni derleyici seçenekleriyle denenebilir
Basit sonsuz döngü tespiti
- GCC 14’e yeni uyarı
-Wanalyzer-infinite-loopeklendi - Örnek kodda iç içe geçmiş
fordöngülerinde ikinci döngünün koşuluj < niken artırma ifadesi yanlışlıklai++olarak kalıyor- Bu, ilk
fordöngüsü kopyalandıktan sonra artırma ifadesindekii’ninjile değiştirilmemesi hatasına karşılık geliyor - Analizör bu yolda
j < nkoşulunun true dalını izlemeyi sürdürerek döngü tekrarını uyarı olarak veriyor
- Bu, ilk
- Mevcut tanı çıktısında akışı anlamak için olay numaralarını
(1)ile(5)arasında sırayla okumak gerekiyor - GCC 15 için, kontrol akışı yolunu vurgulayan ASCII art benzeri bir gösterimle okunabilirliği artırma fikri istek listesinde duruyor
- İlgili kod Compiler Explorer örneğinde denenebilir
Buffer overflow’un metinle görselleştirilmesi
- GCC 13 ile analizör
-Wanalyzer-out-of-boundsüzerinden sınır denetimini desteklemeye başlamıştı - GCC 14’te tahmin edilen buffer overflow’un uzamsal ilişkisi metin tabanlı diyagram olarak gösterilebiliyor
char buf[10]üzerindestrcpy(buf, "hello")yapıldıktan sonrastrcat(buf, " world!")çağrılan örnekte stack tabanlı buffer overflow tespit ediliyor- Önceki mesaj,
bufkapasitesinin 10 bayt olduğunu ve 10. bayttan 12. bayta kadar sınır dışı yazma gerçekleştiğini belirtiyordu - GCC 14’ün diyagramı,
strcpytarafından doldurulan hedef buffer ilestrcatiçin başlangıç noktası olan mevcut sonlandırıcıNULbaytını birlikte gösteriyor
- Önceki mesaj,
- ASCII dışı string örneklerinde de UTF-8 gösterimi izlenerek overflow konumu belirtiliyor
char buf[11]içine"サツキ"kopyalandıktan sonra"メイ"eklenen kod örnek olarak kullanılıyor- Diyagram, overflow’un
メkarakterinin, yani U+30E1’in ortasında gerçekleştiğini gösteriyor
- İlgili kodlar ASCII string örneği ve ASCII dışı string örneği üzerinden görülebilir
C string işlemlerinin izlenmesinin güçlendirilmesi
- GCC 14 analizörü, C string işlemlerini izlemeyi geliştirerek NUL sonlandırıcı baytı ararken buffer tarayan API’leri simüle ediyor
- Pointer, NUL ile sonlandırılmamış bir buffer gösterdiği halde ilgili API’ye iletilen yollar için uyarı veriyor
- Yeni fonksiyon özniteliği
null_terminated_string_arg(PARAM_IDX), belirli bir parametrenin NUL sonlandırılmış string olması gerektiğini analizöre ve kod okuyucularına bildiriyor - Örnekte
example_fn(const char *p)fonksiyonunanull_terminated_string_arg(1)venonnullöznitelikleri eklenmiş durumdachar str[3] = "abc";ifadesinde NUL sonlandırıcı bayt için yer yokexample_fn(str)çağrısında analizör,strsonrasından 1 bayt okuyan stack tabanlı buffer over-read uyarısı veriyor- Tanı çıktısında ayrıca
example_fnfonksiyonunun 1. argümanının NUL sonlandırılmış string pointer olması gerektiğini belirten bir note da yer alıyor
- Bu örnek Compiler Explorer üzerinde çalıştırılabilir
Taint analizinin varsayılan olarak etkinleştirilmesi
- Analizörün taint analizi, saldırgan kontrollü girdileri, arındırma noktalarını ve arındırılmadan kullanılan noktaları izler
- Önceki GCC sürümlerinde, hatalar ve çok sayıda false positive nedeniyle varsayılan olarak etkin değildi ve ayrı bir komut satırı argümanının arkasında gizliydi
- GCC 14’te çeşitli hata düzeltmelerinden sonra
-fanalyzerseçildiğinde taint analizi varsayılan olarak açılıyor - Bu değişiklikle birlikte aşağıdaki 6 taint tabanlı uyarı da etkinleşiyor
CVE-2011-2210 üzerinden bir kernel analiz örneği
- Linux kernelindeki CVE-2011-2210 alıntı örneği, taint analizi vakası olarak kullanılıyor
__SYSCALL_DEFINExmakrosuna__attribute__((tainted_args))eklenerekosf_getsysinfoargümanlarının güven sınırını aşarak geldiği ve tainted değerler olarak ele alınması gerektiği analizöre bildiriliyor- GCC 14 analizörü,
copy_to_user(buffer, hwrpb, nbytes)çağrısında saldırgan kontrollünbytesdeğerinin üst sınır denetimi olmadan boyut olarak kullanıldığını uyarıyor- Tanı çıktısı,
nbytesdeğerininif (nbytes < sizeof(*hwrpb))içinde yalnızca alt sınır kontrolünden geçtiğini gösteriyor copy_to_userfonksiyonunun üçüncü parametresi,access(write_only, 1, 3)özniteliğiyle boyut parametresi olarak işaretleniyor
- Tanı çıktısı,
- Sorun, arındırma koşulunun
if (nbytes < sizeof(*hwrpb))şeklinde yazılmış olması- Amaçlanan koşul
if (nbytes > sizeof(*hwrpb))biçimindeydi - Koşul düzeltilmiş sürümde analizör uyarı vermiyor
- Amaçlanan koşul
- Kernel üzerinde analizörü çalıştırarak zafiyet bulma ve analizördeki false positive’leri düzeltme çalışmaları sürüyor
1 yorum
Hacker News yorumları
Bana göre fanalyzer, Clang’e kıyasla GCC’nin killer özelliklerinden biri. Hataları açıklaması C programlamayı çok daha kolaylaştırıyor; hata mesajlarının geliştirici dostu olması açısından da Rust’a benzemeye başladığını hissettiriyor.
Teknik bir konuyu öğrenmeyi bırakmanın en büyük nedeni çoğu zaman sinir bozucu ya da belirsiz hatalar oluyor. Konuyu biraz dağıttım ama demek istediğim, C’yi seviyorum ve Rust seviyesinde hata mesajları olursa daha da iyi olurdu.
error: missing semicolonyerine şablon örneklemesi ile ilgili 1000 satırdan fazla hata mesajı yağardı.GCC analizörünü birkaç kez kullandım ama çıktıyı okunabilir hâle getiren iyi bir frontend bulamadım. Clang tarafında oldukça iyi HTML çıktısı, CodeChecker, Xcode entegrasyonu gibi çeşitli seçenekler var; GCC tarafındaki çıktıyı nasıl okuduğunuzu merak ediyorum. Üstelik GCC, Clang’den çok daha fazla yanlış pozitif üretiyor gibi görünüyor.
Başka bir başlıkta 36 yorum daha var: https://news.ycombinator.com/item?id=39918278
“GCC 14 Boasts Nice ASCII Art for Visualizing Buffer Overflows (phoronix.com)”, 2 saat önce yazılmış.
Birkaç ay önce küçük bir Linux yardımcı programı yaptım. Herhangi bir çalıştırılabilir dosyanın yerine geçen drop-in bir shim’di; çağrıldığında asıl programmış gibi davranıp orijinali fork ediyor ve stdout/stderr’e bağlıyordu.
Hata çıktısını, o programın bağlamını bilen özel bir GPT asistanına gönderiyordu; asistan da orijinal hatayı insanın okuyabileceği bir forma dönüştürüp shim’in stderr’ine yazıyordu. GCC/Clang’in concept/template ile ilgili iç içe geçmiş derleyici dökümlerini görmekten bıktığım için kullanıyordum, ama isterseniz herhangi bir programla kullanılabilirdi. İyi çalışıyordu ama ciddi şekilde hastalandığım için üzerinde daha fazla çalışamadım; birinin bunu yeniden düzgünce yapıp genelleştirmesi iyi bir proje olabilir.
Analiz sonuçları için daha iyi çıktı biçimleri olsa iyi olurdu. Mevcut yöntem ekran okuyucular için tam bir cehennem.
Çıktı örneğini burada görebilirsiniz: https://godbolt.org/z/aan6Kfxds
Yazıdaki ilk örneğe
-fdiagnostics-format=sarif-stderrkomut satırı seçeneğinin eklenmiş hâli. Diyagramları SVG olarak çıktı alma denemeleri de yaptım ama GCC 14’e koyacak kadar cilalayamadım.Orijinal kod,
nbytes < sizeof(*hwrpb)ise-1döndürüyor,copy_to_user(buffer, hwrpb, nbytes)başarısız olursa-2döndürüyordu. Uygulanan düzeltmenbytes > sizeof(*hwrpb)kontrolüydü, ama bence doğru düzeltmecopy_to_user(buffer, hwrpb, sizeof(*hwrpb)).hwrpbişaretçisindensizeof(*hwrpb)dışında bir boyut kopyalayıp çıkarmak mantıklı değil.nbytes = 4geçirir vesizeof(hwrpb)16 baytsa, çağıran tarafın tamponundan 12 bayt fazla kopyalayarak sahip olunmayan belleği okuyabilir. Bence bundan kaçınmak gerekir.Daha iyi çözüm, çağıran ve çağrılan tarafın ikisinin de desteklediği minimum baytı kopyalamaktır. Örneğin
nbytes = MIN(nbytes, sizeof(hwrpb));gibi.hwrpb->sizeiçindeki sürüm bilgisinin korunduğu varsayımıyla, yapının bir kısmı başlatılmamış olsa bile geriye/ileriye dönük uyumluluk sağlanabilir.nbytes’ten uzun bir değer de geçilemez.Gerçekten harika. Harcanan emek muazzam görünüyor. Zorluk seviyesi, standart kütüphaneye ve C standardına fat pointer/dizi view eklemeye benzer düzeyde görünüyor.
-Wstringop-overflowçok fazla yanlış pozitif verdiği için ilk kapattığım uyarı. Analizör varyantının daha iyi olacağından şüpheliyim.Çok güzel. Bu aralar pek C geliştirmesi yapmıyorum;
strcpyvestrcatne kadar sık kullanılıyor merak ediyorum. Son baktığımda,gotokadar tabu sayılan şeylere yakındılar.Elbette çekirdek geliştirmede
gotonun sıkça tercih edildiğini biliyorum. C string analizinin pratikte ne kadar fayda sağladığını merak ediyorum.gotokullanımı açıkça doğru ve zariftir. Ne pahasına olursa olsun kaçınmaya çalışmak, bakımı zor, çirkin ve dolambaçlı koda yol açabilir. Yaygın olmasa da geçerli kullanım alanları var.strcpygibi işlevler daha az önerilir; ama daha güçlü değişmezlerin, örneğin dil düzeyindeki değişmezlerin bozulmadığı sürece doğru olduğu garanti edilen durumlar da vardır. Böyle bir durum bozulduysa zaten çok daha büyük bir sorun var demektir. Nadir durumlarda, sözde daha güvenli alternatifin hiçbir kazanç sağlamadan biraz daha az verimli olabileceği de savunulabilir.gotokullanımları C’de hâlâ idiomatiktir. Dikkatli olmak gerekir ama sonuçta C, böyle şeyler olabiliyor.Ancak
longjmp’i hiç sevmiyorum.gotokullanımında sorun yok. Buna karşılık strxcpy ailesi tamamen berbat ve hiçbir gerekçeyle kullanılmamalı. Çekirdekte kullanılıyor olmaları korkunç.O işlevler ve onları “düzeltmeye” yönelik tüm başarısız girişimler yörüngeden imha edilmeliydi.
gotodikkatli kullanılırsa sorun değil.strcpyvestrcatda kodun doğru olduğunu biliyorsanız ve yanlışsa büyük bir sorun çıkacağı anlamında “sorun değil”. Ne yazık ki bu açıklama C’nin büyük kısmı için geçerli.gotonunstrcat,strcpykadar tabu olduğunu düşünmüyorum.gotogayet iyi; aynı kapsamda doğru boyuttamallocolmadan kullanılanstrcatvestrcpyise daha çok kod kokusu gibi.Çok iyi. Neyin yanlış olduğunu açıklayan ayrıntılı raporların hepsinin eklenmiş olması sevindirici.