10 puan yazan GN⁺ 2025-11-19 | 1 yorum | WhatsApp'ta paylaş
  • safe_c.h, C diline C++ ve Rust’ın güvenlik ile kullanım kolaylığı özelliklerini ekleyen 600 satırlık özel bir başlık dosyasıdır ve bellek sızıntısı olmayan thread-safe grep (cgrep) uygulamasında kullanıldı
  • RAII, akıllı işaretçiler, otomatik temizleme (cleanup) özniteliği sayesinde manuel free() çağrıları olmadan kaynak yönetimini otomatikleştirir
  • Vektörler, görünümler, Result tipi, sözleşme makroları ile buffer overflow, hata işleme ve önkoşul doğrulamasını güvenli şekilde gerçekleştirir
  • Mutex otomatik serbest bırakma, thread spawn makroları, dal tahmini optimizasyonu ile eşzamanlılık ve performansı korurken güvenlik sağlar
  • Sonuç olarak aynı performansla (-O2 düzeyinde) sızıntısız ve segfault’suz C kodu yazmanın mümkün olduğunu gösterir

safe_c.h genel bakış

  • safe_c.h, C++ ve Rust özelliklerini C koduna taşıyan bir başlık dosyasıdır
    • C23 [[cleanup]] özniteliğini desteklemeyen derleyicilerde bile (GCC 11, Clang 18 vb.) aynı RAII (otomatik temizleme) davranışını sunar
    • CLEANUP(func) makrosu ile fonksiyon sonunda kaynaklar otomatik serbest bırakılır
    • LIKELY() ve UNLIKELY() makroları ile hot path dal tahmini optimizasyonu sağlar

Bellek yönetimi: UniquePtr ve SharedPtr

  • UniquePtr, scope bittiğinde otomatik olarak free() çağıran tek sahipli akıllı işaretçidir
    • AUTO_UNIQUE_PTR() makrosu ile tanımlandığında, hata oluşsa veya erken dönüş olsa bile bellek otomatik serbest bırakılır
  • SharedPtr, son referans bırakıldığında kaynağı otomatik yok eden otomatik referans sayımlı bir yapıdır
    • shared_ptr_init() ve shared_ptr_copy() ile referans artırma/azaltma otomatik işlenir
    • Thread’ler arasında paylaşılan güvenli struct yönetiminde kullanılır

Buffer overflow önleme: Vector ve View

  • DEFINE_VECTOR_TYPE() makrosu ile tip güvenli, otomatik genişleyen vektörler oluşturulur
    • Yeniden tahsis, kapasite yönetimi ve temizleme (cleanup) otomatik yürütülür
    • AUTO_TYPED_VECTOR() ile tanımlandığında scope sonunda otomatik serbest bırakılır
  • StringView ve Span, ayrı bir malloc olmadan string ve dizi dilimlerini işleyen sahiplik almayan referans yapılarıdır
    • DEFINE_SPAN_TYPE() ile tipe özel Span tanımlanır
    • Sınır kontrolleri içerdiği için güvenli dizi erişimi sağlar

Hata işleme: Result tipi ve RAII

  • Result yapısı, Rust’taki Result<T, E> benzeri başarı/başarısızlık ayrımlı dönüş tipidir
    • DEFINE_RESULT_TYPE() ile tipe özel sonuç yapıları oluşturulur
    • RESULT_IS_OK() ve RESULT_UNWRAP_ERROR() ile açık hata işleme sunar
  • CLEANUP özniteliği ile birlikte kullanıldığında fonksiyon sonunda kaynaklar otomatik serbest bırakılır
    • AUTO_MEMORY() makrosu, malloc ile ayrılan belleği otomatik temizler

Sözleşmeler ve güvenli string’ler

  • requires() / ensures() makroları ile fonksiyonların önkoşul ve sonkoşulları açıkça belirtilir
    • Başarısızlık durumunda net hata mesajı yazdırılır
  • safe_strcpy(), buffer boyutu kontrolü içeren bir kopyalama fonksiyonudur ve overflow’u önler
    • Başarısız olduğunda false döndürerek güvenli hata işleme sağlar

Eşzamanlılık: otomatik kilit açma ve thread makroları

  • CLEANUP tabanlı otomatik mutex serbest bırakma fonksiyonu deadlock’u önler
    • Scope bittiğinde pthread_mutex_unlock() otomatik çağrılır
  • SPAWN_THREAD() ve JOIN_THREAD() makroları ile thread oluşturma ve join işlemleri basitleştirilir
    • cgrep içindeki dosya işleme thread pool uygulamasında kullanılır

Performans optimizasyonu

  • LIKELY() / UNLIKELY() makroları ile hot path dal tahmini sağlar
    • -O2 derlemelerinde bile PGO düzeyinde optimizasyon etkisi elde edilir
  • Güvenlik özellikleri eklense de performans kaybı yoktur

Sonuç

  • safe_c.h kullanan cgrep, 2.300 satırlık C koduyla 50’den fazla manuel free() çağrısını ortadan kaldırır
  • Aynı assembly ve çalışma hızını korurken bellek sızıntısı ve segfault olmayan güvenli C kodu ortaya koyar
  • C’nin sadeliğini ve özgürlüğünü korurken modern güvenliği birleştiren bir örnek sunar
  • Yazar, sonraki yazısında cgrep’in neden ripgrep’ten 2 kattan fazla hızlı ve bellek kullanımında 20 kat daha verimli olduğunu ele almayı planlıyor
  • safe_c.h’nin yeni projeler için uygun olduğu, ancak makro tabanlı yapısı nedeniyle debug etmenin zorlaşabileceği belirtiliyor
  • Çeşitli statik analiz araçlarıyla (GCC analyzer, ASAN, UBSAN, Clang-tidy vb.) doğruluk ve güvenlik doğrulaması yapılmış

1 yorum

 
GN⁺ 2025-11-19
Hacker News yorumları
  • Bu yazı, C'de güvenli soyutlamalar (safe abstraction) uygulanırken ortaya çıkan maliyet sorununu gösteriyor.
    Paylaşımlı işaretçi uygulaması POSIX mutex kullandığı için (1) platformdan bağımsız değil ve (2) tek iş parçacığında bile mutex ek yükü ödetiyor.
    Yani bu, bir zero-cost abstraction değil.
    C++'ın shared_ptr'ı da aynı soruna sahip, ancak Rust bunu Rc ve Arc olmak üzere iki ayrı türle ayırarak çözüyor.

    • C++ shared_ptr, mutex değil atomic işlemler kullanır.
      Rust'taki Arc'a benzer; blogdaki uygulama sadece verimsiz.
      Yine de C++'ta Rc karşılığı bir tür olmadığı için, yalnızca basit bir referans sayımlı işaretçi istendiğinde bile hâlâ maliyet oluşur.
    • glibc ve libstdc++ ortamlarında pthreads ile linklenmezse shared_ptr thread-safe değildir.
      Çalışma anında pthread sembollerini bulup atomic veya non-atomic yolunu seçer.
      Bence her zaman atomic kullanmak daha iyi olurdu.
    • Bana göre asıl önemli olan kodun çökmemesini sağlamak.
      Çapraz platform desteği çoğu durumda sadece "olsa iyi olur" seviyesinde.
      Mutex ek yükü can sıkıcı ama modern CPU'larda katlanılabilir düzeyde.
      Rust'ın harika olduğunu biliyorum ama C ekosistemi o kadar büyük ki onu tamamen değiştirmek zor.
    • Referans sayımı mutex yerine C11 atomic işlemleri ile de gerçekleştirilebilir.
      Bu durumda mutex'in ne avantaj sağladığını pek anlayamıyorum.
    • POSIX mutex zaten birçok platformda uygulanmış durumda, bu yüzden bence aslında daha genel amaçlı bir API.
  • Fil'in (aka pizlonator) yaptığı, C'yi bellek açısından güvenli hâle getiren FUGC adlı bir garbage collector projesi var.
    Mevcut koda neredeyse hiç dokunmadan uygulanabiliyor ve C/C++'ı bellek güvenli diller hâline getiriyor.
    ilgili HN gönderisine ve resmî siteye bakılabilir.

    • Bu sayede bu projeyi ilk kez öğrenmiş oldum. Gerçekten harika bir deneme gibi görünüyor.
    • Ama garbage collector'ün getirdiği performans kaybını göze almak istemem.
  • Bu yazı, bellek güvenliğinin özünü biraz yanlış ifade ediyor gibi görünüyor.
    Yerel değişkenlerin otomatik serbest bırakılması ya da sınır denetimleri tek başına yeterli değil.
    Asıl mesele, program genelindeki bellek ömrü yönetimi.
    Örneğin UniquePtr döndürürken ya da SharedPtr kopyalarken referans sayımını unutup unutmadığınız veya intrusive list içindeki öğelerin ömrünü kimin yönettiği gibi konular.
    Sonuçta bu yaklaşım bana eski #define xfree(p) kalıbından çok da farklı gelmiyor.

    • UniquePtr, struct'ı değer olarak döndürebildiği için mümkün.
      Ama SharedPtr kopyası referans sayımını artırmayı otomatik olarak yapmıyor.
    • #define xfree(p) kalıbının neden kötü olduğunu merak ediyorum.
  • C23'ün [[cleanup]] özniteliğini getirdiği söylense de, gerçekte bu GCC uzantısı ve [[gnu::cleanup()]] olarak yazılmalı.
    örnek koda bakılabilir.

    • Bununla ilgili bilgi bulmak zordu; sonuçta sadece sözdizimi değişmiş gibi görünüyor, işlevin kendisi hâlâ uzantı.
  • “C++: Bakın diğer diller gücümün küçük bir kısmını bile taklit etmek için ne kadar uğraşıyor” diye bir şaka vardı.
    Makrolarla neden C++ taklit edilmeye çalışıldığını merak ediyorum ama yine de ilginç bir deneme.

    • C++'ın tüm özelliklerini eklemeden daha güvenli bir C oluşturma süreci ilginçti.
      Ama sonuçta C++17 özelliklerine kadar öykünülüyorsa, doğrudan C++ kullanmak daha iyi olmaz mı diye düşünüyorum.
    • Ben parse edilebilir bir dil istiyorum.
      C hâlâ çalışması kolay bir dil ama C++ o kadar karmaşık ki frontend olmadan yaklaşmak zor.
    • C, basit olduğu için hack'lemesi keyifli bir dil.
      C++'a geçince build zinciri, name mangling, libstdc++ bağımlılığı gibi nedenlerle işler karmaşıklaşıyor.
    • Bu proje, C++'ın bazı özelliklerine izin verip sınırlı bir sözdizimini zorunlu kılabilir.
      Buna karşılık C++'ı C tarzında kullanırsanız böyle bir kısıtlama olmaz.
    • Gömülü CPU üreticilerinin C++ derleyicisi sunmaması da pratik bir kısıt.
  • setjmp/longjmp tabanlı istisna işleme ile uyumlu değil.
    Bunun yerine POSIX'in pthread_cleanup_push yapısından esinlenen bir cleanup makro çifti ile entegre edilebilir.
    cleanup_push(fn, type, ptr, init) ve cleanup_pop(ptr) kullanılarak yığın tabanlı temizleme rutinleri uygulanıyor.
    Bu yöntemin avantajı, denge hatalarını derleme zamanında yakalayabilmesi.

  • safeclib içindeki gerçek safec.h ile karıştırmamak gerekir.
    safeclib header'ına bakılabilir.

    • Annex K uygulamasını neden bakımda tutmaya çalıştıklarını merak ediyorum.
      Global constraint handler yüzünden tasarım hatası olarak değerlendiriliyor ve çoğu toolchain bunu desteklemiyor.
      ilgili belgeye bakılabilir.
  • Nim dilini kullanırsanız safe_c.h'nin sunduğu her şeyi elde edebilirsiniz.
    Nim, C'ye derlenir ve güvenlik ile performansı aynı anda sunar.
    ARC tabanlı otomatik referans sayımı, defer, Option[T], bounds-checking, likely/unlikely gibi çeşitli özellikleri varsayılan olarak sağlar.
    resmî site, ARC tanıtımı, view types, Option belgeleri, likely şablonu incelenebilir.

  • Bu yaklaşımın hedefi taşınabilirlik ise, gerçekte C99'da kalmak daha güvenli.
    MSVC'nin C derleyicisi zorlayıcı olsa da çapraz platform için neredeyse vazgeçilmez.
    Ben de benzer bir header yazdım ama taşınabilirlik sorunları yüzünden cleanup yardımcılarını eklemedim.

    • Makroların C++ kodu (destructor tabanlı) üretmesini sağlarsanız cleanup özniteliği olmadan da yapılabilir.
      C kodu aynı zamanda C++ olarak da derlenebiliyorsa iyi çalışır.
    • Windows'ta da MSYS2 + GCC ile rahatça geliştirme yapılabilir.
      Paket yöneticisi de beraberinde gelir.
    • Bu arada MSVC artık C17'yi destekliyor.
  • Yazıda birkaç kez geçen cgrep için kod bağlantısı yok.
    GitHub'da aynı isimde birçok proje var ama çoğu farklı dillerde yazılmış.

    • Ben de hangi cgrep'ten bahsedildiğini bilmiyorum ve kendim denemek isterdim.