2 puan yazan GN⁺ 2024-12-22 | 1 yorum | WhatsApp'ta paylaş
  • Scylla, mevcut C kodunu unsafe Rust’a taşıyıp sonradan düzeltmek yerine, özgün C kodunu kademeli olarak yapılandırarak doğrudan güvenli Rust’a derlenebilir bir yol öneriyor
  • Hedef, veri işleme, pointer işlemleri, yapılandırılmış denetim akışı ve taşınabilir kod içeren uygulamalı C alt kümesi; goto, tamsayı-pointer cast’leri, pointer hileleri, bit alanları ve untagged union’a bağımlı kodlar kapsam dışında
  • Dönüşüm, Clang AST’sini Mini-C’ye indirgedikten sonra güvenli Rust’a aktaran iki aşamalı bir süreç; tamsayı yükseltme, örtük dönüşümler, atama ifadeleri, prefix/postfix artırma gibi C’nin incelikli davranışlarını açık hâle getiriyor
  • En zorlu konu olan pointer aritmetiği, Rust slice’ları ve split_at_mut/split_at tabanlı bölme ile değiştiriliyor; mutability çıkarımı, trait türetme, Box ve borrow seçimi de birlikte ele alınıyor
  • Windows SymCrypt, HACL★, bzip2’nin çekirdek sıkıştırma algoritması, EverParse CBOR parser/serializer’ı ve Microsoft FrodoKEM’in bazı bölümleri değerlendirildi; dönüşüm sırasında bzip2 ve FrodoKEM’de undefined behavior da bulundu

Scylla’nın seçtiği C→Rust geçiş yolu

  • Mevcut C kodunu Rust’a taşıma motivasyonu bellek güvenliği sorunundan doğuyor
    • Google ve Microsoft araştırmaları, güvenlik açıklarının %70’inin hatalı bellek yönetimiyle ilişkili olduğunu tahmin ediyor
    • Şirketler ve kamu kurumları, güvenlik açısından kritik sistemlerde Rust gibi bellek güvenli dillerin kullanılmasını öneriyor
  • Rust yeni kod için açık avantajlar sunsa da, zaten test edilmiş ve debug edilmiş endüstriyel C kodunun tamamını yeniden yazmak zor
  • Mevcut C→Rust otomatik dönüşüm araçları, C’nin tamamını desteklemek için çoğunlukla unsafe Rust üretiyor
    • unchecked pointer’lar veya Rust’ın transmutation’ı gibi C tarzı kalıplara izin veriyor
    • Statik bellek güvenliği garantisi ortadan kalktığı için safe language kullanma amacı zayıflıyor
  • Yaygın iş akışı, unsafe Rust çıktısını başlangıç noktası olarak alıp sonrasında güvenli Rust’a doğru tekrarlı refactoring yapma şeklinde
    • raw pointer’ları safe Rust borrow’larına çevirmek veya düşük seviyeli temsillerden Rust soyutlamalarını geri kazandırmak için statik analizler önerilmiş durumda
    • Refactoring araçları dağınık; c2rust refactor desteği de 2022’de durduruldu
  • Scylla ise üretilen unsafe Rust’ı düzeltmek yerine, özgün C kodunun kendisini kademeli olarak yapılandırıp safe Rust’a derlenebilir hâle getirmeyi seçiyor

Desteklenen C alt kümesi ve kapsam dışı desenler

  • Scylla’nın hedefi, öngörülebilir dönüşüm ve özgün C’ye yakın Rust kodu üretmek
  • Desteklenen alan uygulamalı C alt kümesi
    • Veriyi manipüle eden ve işleyen kod
    • Pointer aritmetiği kullanan kod
    • Yapılandırılmış denetim akışına sahip kod
    • Taşınabilir kod
  • Aşağıdaki desenlere bağımlı kod tabanları desteklenmiyor
    • goto
    • Tamsayı-pointer cast’i yoluyla nesne temsilini kullanma
    • Pointer hileleri
    • Bit alanları
    • untagged union
  • Geliştiriciler, Scylla’nın anlayabilmesi için C kaynaklarına hedefli rewrite ve annotation uygulayabilir
    • Rust borrow checker ile uyuşmayan aliasing desenleri yeniden yazılabilir
    • tagged union’ın üst seviye ADT’ye çevrilmesi gerektiği bilgisi Scylla’ya sağlanabilir

Mini-C: C’nin belirsizliklerini azaltan ara dil

  • Scylla, Clang frontend AST’sinden başlayıp önce Mini-C adlı dile dönüştürüyor
  • Mini-C, C gibi branch, loop, pointer, dereference ve adres alma işlemlerini ele alıyor; ancak “sürprizsiz” semantiğe sahip
    • Tüm tamsayılar sabit genişliğe sahip
    • C’deki integer promotion ve integer conversion açık cast olarak ifade ediliyor
    • void * gibi untyped pointer’lara izin verilmiyor
  • Mini-C, C’den farklı olarak bir ifade dili
    • Atama bir değer döndürmüyor
    • e1 = e2 = e3, p[i++] gibi C yapıları Mini-C’de desugar ediliyor
    • Loop ve koşul ifadelerinin test ifadeleri C’deki int değil, bool tipinde olmalı
  • Clang’in typed AST’si Mini-C typed AST’sine taşınırken C’nin örtük davranışları açık hâle getiriliyor
    • Koşul ifadeleri bool’a uyduruluyor
    • Dizi indeksleri size_t’ye uyduruluyor
    • Fonksiyon çağrısı argümanlarındaki ve atamanın sağ tarafındaki örtük dönüşümler açık cast’e çevriliyor
    • Aritmetik işlemlerde C standardındaki usual arithmetic conversions yansıtılıyor
  • Dönüşüm, C kodunun taşınabilir olduğunu ve C veri modeline bağımlı olmadığını varsayıyor
    • Örneğin long’un 4 bayt mı 8 bayt mı olduğuna göre davranışı değişen kod beklenmiyor
    • Uygulama, configure-time’da hedef mimarinin veri modelini algılayıp unsigned int’i uint32_t gibi sabit genişlikli tiplere dönüştürüyor

ADT ve tuple sentezi

  • Mini-C, C’den daha yüksek seviyede ADT, tuple ve pattern matching sunuyor
  • tagged union’lar annotation yoluyla ADT’ye çevriliyor
    • Hedef biçim { int tag; union { t0 case0; ...; tn caseN }} gibi bir yapı
    • tag değeri 0’dan N’ye kadar ve tag değerinin union case sırasıyla eşleştiği varsayılıyor
  • Scylla, annotated tagged union tiplerini variant tiplerine dönüştürüyor
    • Değer oluşturulurken .tag = i ile .casej = e değerlerinin eşleşip eşleşmediğini denetliyor
    • Eşleşirse ilgili constructor değerine dönüştürüyor
    • payload ile tag uyuşmuyorsa Mini-C’ye çevirmiyor
  • tagged union alanlarına erişimin güvenli olması için mevcut tag durumunun bilinmesi gerekiyor
    • if (x.tag == i) { ... x.casei } veya switch gibi desenleri tanıyor
    • Bunları match x with | Ci v -> ... biçimine dönüştürüyor
    • Başka bir union case’e erişim invalid kabul ediliyor ve dönüşüm hatası veriliyor
  • tuple da annotation ile sentezlenebiliyor
    • n alanlı bir struct, n-ary tuple’a dönüştürülüyor
    • Alan erişimi tuple field access’e çevriliyor
    • tuple yapısal olarak tiplendirildiği için mut-polymorphism avantajlarından yararlanabiliyor

Mini-C’den safe Rust’a dönüşüm

  • Mini-C, tip annotation’ları eksiksiz bir C program temsili sunuyor; ardından safe Rust’a dönüştürülüyor
  • Başlıca zorluk üç tane
    • C’nin pointer aritmetiğini ortadan kaldırmak
    • Mutability ve aliasing’i açık hâle getirmek
    • Trait gibi Rust’a özgü yapıları otomatik sağlamak
  • Pointer tipi dönüşümü, Rust’ın pointer temsillerindeki farklar nedeniyle karmaşık
    • Rust Box<T> ile &T arasında ayrım yapıyor
    • Tek elemanlı pointer ile çok elemanlı pointer da &T ve &[T] gibi ayrılıyor
    • Rust dizileri değerdir ve C’deki gibi otomatik olarak pointer’a decay etmez
  • Temel strateji, C’deki tüm pointer type’ları Rust’ın slice borrow’u &[T] olarak derlemek
    • Stack pointer’lar ve heap pointer’lar varsayılan olarak slice borrow oluyor
    • Tek elemanlı ve çok elemanlı pointer’lar da varsayılan olarak slice borrow oluyor
    • Mutability ayrı bir aşamada otomatik çıkarılıyor
  • Scylla, heuristics ve elle annotation ile bazı pointer’ları Box<T> olarak çeviriyor
    • Global referansı olmayan ve fresh allocation olduğu düşünülen T *create() gibi fonksiyonlar fn create() -> Box<T> olarak çevrilebilir
    • Bu analiz, struct ve variant tanımlarının içine kadar sabit nokta yöntemiyle özyinelemeli uygulanıyor
    • Borrow kalan struct’lar lifetime parameter alıyor

Box, slice ve array dönüşümlerinin kısıtları

  • Rust’ta array, slice borrow ve Box arasında açık dönüşümler gerektiğinden Scylla’nın Rust dönüşümü de tip odaklı çalışıyor
  • Dönüşüm kuralları, array veya boxed slice’ı slice borrow’a çeviren coercion’lar ekliyor
    • Array, &x[..] gibi bir biçimde slice borrow oluyor
    • Boxed slice borrow’a dönüştürülebiliyor
  • Ters yönde dönüşüm de mümkün
    • Slice veya array heap allocation’a yükseltilip Box<[T]> hâline getirilebilir
  • Bu ters yönlü dönüşüm kopyalama semantiği farkı yaratabilir
    • C’de array ve pointer aynı belleği gösterebilir
    • Rust’ta Box::new(x), x’in bir copy’sini oluşturabilir
    • Tamsayı dizileri gibi temel tip array’leri için Copy trait’inden opt out etmenin yolu yoktur; Rust sessizce kopyalama yapabilir
  • Scylla böyle bir dönüşüm gerçekleştiğinde özgün değişkeni ortamdan kaldırarak daha sonra kullanılmasını yasaklıyor
    • Özgün C programı o değişkeni kullanmaya devam ederse dönüşüm hatası oluşuyor
    • Geliştirici, dönüşümden önce C kaynak kodunu düzelterek niyeti daha belirgin hâle getirmeli

Pointer aritmetiğini Rust slice bölmesine çevirmek

  • C programları dizilere yalnızca tek bir base pointer üzerinden erişmez; diziyi chunk’lara bölme veya mevcut konum pointer’ını tutarak dolaşma desenlerini sıkça kullanır
  • Rust keyfi pointer aritmetiğine izin vermez; bunun yerine split_at_mut veya split_at ile slice bölme yöntemi sunar
    • split_at_mut, özgün slice’ın sahipliğinden vazgeçip iki sub-slice elde eden primitive’dir
    • Mutable data’nın tek owner’a sahip olması gerektiği Rust invariant’ını korur
  • Scylla, C pointer aritmetiğini Rust’ın bölme yöntemiyle eşleştirmek için split tree kavramını getiriyor
    • Her C pointer tek bir split tree’ye eşleniyor
    • split tree flow-dependent olarak değişiyor
    • Belirli bir program noktasında C pointer erişiminin hangi Rust slice erişimine çevrilmesi gerektiğini izliyor
  • C pointer’larında uzunluk bilgisi bulunmadığından Scylla, chunk’ların çakışmadığını varsayıyor
    • Çakışma amaçlanıyorsa Rust tip denetiminden geçemez ve geliştiricinin C kodunu yeniden yazması gerekir
    • Dönüşüm öngörülebilir olmalı; bu yüzden backtracking’den kaçınıp forward biçimde yürütülüyor
  • Örnekte 32 baytlık abcd array’i dört adet 8 baytlık limb bölgesine ayrılıyor
    • C’de abcd + 0, abcd + 16, abcd + 8, abcd + 24 gibi soldan sağa sırada olmayan pointer aritmetiği kullanılıyor
    • Rust dönüşümü, doğru sub-slice’ı bulmak için split_at_mut çağrılarının geçmişini split tree’de tutuyor

Değerlendirilen hedefler ve bulunan undefined behavior

  • Scylla uygulaması, mevcut C kodunu girdi olarak almak ve safe Rust çıktısı üretmek için Clang kullanıyor
  • Değerlendirme hedefleri, çeşitli mevcut C projelerinin bazı bölümlerini kapsıyor
    • Windows’un SymCrypt’inin bazı bölümleri
    • HACL★ kripto kütüphanesinin bazı bölümleri
    • bzip2 sıkıştırma algoritmasının çekirdek kısmı
    • EverParse kütüphanesinin CBOR binary parser ve serializer’ı
    • Microsoft’un FrodoKEM post-quantum cryptographic primitive implementasyonu
  • Bu örnekler, Scylla’nın uygulamalı C alt kümesinin güvenlik açısından hassas birçok uygulamayı kapsayabileceğini gösteriyor
  • Dönüşüm sürecinde bzip2 ve FrodoKEM’in özgün C kodunda bulunan undefined behavior da tespit edilip raporlandı

1 yorum

 
GN⁺ 2024-12-22
Hacker News yorumları
  • Bu çalışmanın “zaten biçimsel olarak doğrulanmış C kod tabanlarını” hedeflediği noktası önemli.
    Tipik sistem C kodu biçimsel olarak doğrulanmış değildir; dolayısıyla bu epey farklı bir hikâye.

    • Buna rağmen tamamen güvenilir gibi görünmüyor. Makalenin 2.2 bölümünde de dönüşüm kurallarının getirdiği zorlamaların incelikli anlam farkları yaratabileceği söyleniyor.
      Örneğin yığında bulunan bir C dizisini gösteren bir işaretçi, Rust’ta Box<[u8]> ile yeni bir heap kopyasının sahibi olan işaretçi gibi çevrilebilir. Orijinal kod işaretçinin gerçekten o diziyi gösterdiği gerçeğine dayanıyorsa, çevrilmiş kod sessizce yanlış çalışabilir.
      Benim projem scpptool’un bellek güvenli C++ alt kümesini otomatik çevirme özelliği olsaydı, diziyi alternatif tipe ve yineleyicilere taşıyıp özgün anlamı koruyacak şekilde ele alırdı.
      OP’nin projesi yalnızca güvenli Rust’a çevrilmesi kolay C ile ilgileniyor olabilir; ama problemin zorluğu düşünülürse sonuç saygıyı hak ediyor ve bir ölçüde faydalı da görünüyor.
    • Bunda çok daha fazla uyarı var ve neredeyse abartılı pazarlama sayılır.
      Başta, aslında gerçek C’yi çevirmemişler; F* ile yazılmış kodu, C derleyicisi tarafında Rust çıktı verecek şekilde değiştirmişler. Karmaşık gerçek C koduyla uğraşmamışlar; olsa olsa oyuncak bir derleyicinin üreteceği türden sınırlı Mini-C ile ilgilenmişler.
      Orijinal metinde de özgün C programı x’e daha fazla bağımlıysa çevirinin hata vereceği ve programcıdan kaynakta değişiklik isteyeceği söyleniyor; bu da C’nin zaten Rust ödünç alma denetleyicisini memnun edecek bir tarzda yazılmış olmasını umdukları anlamına geliyor.
      “Şekil 4’te güzel kurallar sunuyoruz ama gerçek uygulama sayısız hileye dayanıyor” şeklinde akademik bir ifade gibi görünüyor.
      Daha kötüsü, statik olarak ayırt edilebilen örtüşmelerde derleme hatası verdiklerini, ayırt edilemeyenlerde ise Rust kodunun çalışma zamanında panic edebileceğini söylüyorlar. Biçimsel olarak doğrulanmış bir C programını “artık çökebilir de” bir Rust programına dönüştürmek garip.
      HACL*’ı mevcut bir biçimsel olarak doğrulanmış C kod tabanı diye adlandırmak da isabetli değil. HACL* C’ye derlense de bir C kütüphanesi değil; bambaşka bir dilde yazılmış.
      Dürüst başlık “F*’ın bir alt kümesini kısmen güvenli Rust’a derlemek, kısmen biçimselleştirilmiş” gibi bir şey olmalıydı.
    • Rust’ın kendisi biçimsel olarak doğrulanmış mı? Bildiğim kadarıyla değil.
    • Biçimsel olarak doğrulanmış C tam olarak nedir ve neden daha fazlası yok, merak ediyorum.
    • Temel farkın ne olduğunu merak ediyorum. Uyumluluk derleyici bayrağıyla zorunlu kılınabilir mi?
  • 2002’de araştırmacılar C’nin güvenli bir lehçesi olan Cyclone üzerine bir makale yayımladı ve C kodunu Cyclone’a elle taşırken mevcut C kodundaki güvenlik hatalarını keşfettiler.
    Bu tür elle ya da otomatik C dönüşümleri, yalnızca daha güvenli dillerin benimsenmesini artırmakla kalmaz, mevcut hataları ortaya çıkarma potansiyeli de taşır.
    [1] https://www.researchgate.net/profile/James-Cheney-2/publicat...

    • Cyclone artık desteklenmiyor, çekirdek araştırma projesi de sona erdi ve geliştiriciler başka işlere geçti.
      Cyclone’daki birçok fikir Rust’a girdi; kod biraz uğraşla çalışır hâle getirilebilir ama modern 64 bit platformlarda doğrudan derlenmiyor.
      http://cyclone.thelanguage.org
  • C2Rust’ı ilk adım olarak kullanıp, C projeleri dâhil birkaç projeyi Rust’a taşıdım ve birkaç sonuca vardım.

    1. C programını Rust’a taşıdığınızda, unsafe içerse bile Rust’ın güçlü kısıtları — örneğin sınır denetimi ve katı imzalar — sayesinde hatalar çoğu zaman hızlıca ortaya çıkıyor.
    2. Otomatik C→Rust dönüşümünün tamamen çözülebileceğini sanmıyorum. C programlarının tasarımı Rust’tan temelden farklı ve güvenli hâle getirmek için ciddi ölçüde yeniden tasarım gerekiyor.
    3. Bazı durumlarda, tam anlamı koruyarak C’den Rust’a taşımak imkânsız. Çünkü güvensizlik tasarımın kendisine içkin olabilir.
      Yine de araçlar port etme için vazgeçilmez ve araçlar geliştikçe süreç daha pürüzsüz hâle gelecek.
    • “Hızlı ve güvenli Rust”a otomatik dönüştürmek zor; ama genel anlamda güvenli Rust’a otomatik dönüştürmek çok daha kolay.
      Belleği bir dizi olarak temsil edip işaretçileri bu dizinin indeksleri olarak ele almak yeterli. Böylece denetimsiz işaretçi aritmetiği veya union gibi C davranışlarını ödünç alma denetleyicisiyle kavga etmeden ifade edebilir ve anlamı da koruyabilirsiniz. C→Java’da da uzun zamandır benzer teknikler kullanılıyor.
      Elbette böyle bir dönüşümün değeri tartışmalı. Esasen C’yi wasm’ye derlemeye benzer ama daha yavaştır; üretilen kod teknik olarak “güvenli” olsa bile, buffer overflow’un hatalı durum oluşturması ya da dangling pointer’ın izin verilmemesi gereken bağlamlarda veriye erişimi mümkün kılması gibi sorunlar kalır.
    • “Güvensizliğin tasarıma içkin olabileceği” sözüne ilkesel olarak katılıyorum ve kendi deneyimim de bunu güçlü biçimde hissettiriyor; ama tartışmayı somutlaştıracak basit bir örnek olsa iyi olurdu.
  • Yazarıyım. Çeşitli thread’lerde ortaya çıkan birkaç noktayı toparlamanın faydalı olacağını düşünüyorum

    1. Bu, arxiv’e yüklediğimiz akademik bir makale; C→Rust sorununu çözdüğünü iddia eden yeni bir ürün duyurusu değil. PL konferansına sunuldu ve FOSDEM gibi açık kaynak etkinliklerindeki sunumlardan farklı bir kitleye ve beklenti düzeyine sahip
    2. Yaklaşım basit. C’yi güvenli Rust’a çevirme kısıtından yola çıkıp, iyi çalışan küçük bir C alt kümesine, slice bölme çıkarımına, hata verebilen çevirilere, durdurulabilen programlara vb. ihtiyaç olduğunu inceliyoruz. Sahip olduğumuz hedef olan F* içine gömülü C üzerinde değerlendirdik ve Firefox, Python gibi ana akım yazılımlarda kullanılan büyük C kütüphanelerinde bu kısıt altında oldukça ölçeklenebildiğini gösterdik. Firefox’u otomatik olarak Rust’ta yeniden yazabileceğimizi iddia etmiyoruz
    3. Araştırma zaten böyle ilerler. Tasarım uzayında ilginç bir nokta olduğunu görüyoruz; tüm sorunları çözdüğümüzü iddia etmiyoruz ama C→Rust çevirisi alanında daha fazla ilerlemenin önünü açabilecek bir fikir olduğunu düşünüyoruz. Mevcut bir araç bu yaklaşımı alt kümeye uyan kodlar için kullanıp, uymayan kısımlarda unsafe Rust’a geri dönebilir
    4. Bu son sürüm değil. Gerçek C front-end’ini libclang ile oluşturuyoruz ve üretilen Rust’ın sınır dışı erişim üretmemesini garanti etmenin yollarını da araştırıyoruz. Örneğin doğrulama koşullarını Z3’e dışa aktarmayı düşünüyoruz. Hakemler daha fazla çalışma gerektiğini düşünürse geliştirip tekrar sunarız; alanın aktif olduğunu ve başkalarının bu fikirlerden yararlanabileceğini düşünüp makaleyi kabul ederlerse daha da iyi olur
  • Asıl merak ettiğim, bunu neden böyle yapmamız gerektiği
    Endüstriyel uygulamaları C’den Rust’a gerçekten dönüştürebilecek bir teknoloji ise, mevcut C uygulamalarını daha kolay kurşun geçirmez hâle getirmeye de yarayabilir gibi. Statik analizörlere veya test üreticilerine konacak analizleri yapmak yeterli olur
    Benzer şekilde güvenli wrapper’lar üreterek, yeni kodun doğrulanmış C’nin yanında Rust ile yazılmasını da sağlayabilir. Yeni kod Rust’ın avantajlarını alır, mevcut kodun güvenli olduğu doğrulanır ve arayüzler de daha güvenli hâle gelir
    Tam bir çevirici ideal olabilir. Uzun vadede kod tabanının tek dilde olması iyi olur. Ama mevcut C/C++ için düşük false positive oranına sahip, tek düğmeyle güvenlik sağlama ihtiyacı hâlâ en büyük ihtiyaç. Google’ın derleyici araçları veya ForAllSecure’un Mayhem’i gibi C içinde kötü yapıları otomatik düzeltmek de mümkün olabilir

    • Bazı C programları güvenli hâle getirilemez; bu yüzden “endüstriyel uygulamaları Rust’a dönüştürebilecek bir teknoloji, C uygulamalarını daha kolay kurşun geçirmez hâle getirebilir” ifadesi doğru değil
      Bunun nedeni tanımsız davranışa veya belirtilmemiş davranışa dayanması olabilir; ya da uygun güvenlik kontrolleri eklendiğinde kabul edilebilir girdi alanının kullanışsız olacak kadar daralması olabilir
      Güvenli bir dile çevirmek, girdilerin ifade gücünü korurken çalışma zamanında doğru davranışı statik olarak garanti edebildiği için bu tür durumlarda nesnel olarak daha iyidir
      “Sahada kanıtlanmış C” diye bir şeyin var olduğunu söylemek de, sayısız kritik zafiyetin gösterdiği gibi zor. Gerçekte var olan şey, yeterince sık oldukça iyi çalıştığı için kullanışlı görünen C’den ibaret
      Eski kodun güvenli olduğu kanıtlanmış değildir; şans eseri güvenli olduğu varsayılır. “Kanıt” özellikle böyle bir makale bağlamında belirli bir anlama sahiptir ve C kodlarının ezici çoğunluğu katı matematiksel ölçütlerle kanıtlanmış değildir. Buna karşılık Rust tip sisteminin matematiksel olarak doğru olduğu kanıtlanmıştır
      Tam çevirici, nelerden vazgeçebileceğinize bağlıdır. Performanstan, girdi alanından, çıktı aralığından, kod okunabilirliğinden vb. vazgeçilebiliyorsa bir ölçüde mümkün olur; ama tüm bu açılardan sound ve complete bir çevirici istemeye başladığınızda sorun çıkar
  • Safça Rust’a çevirdiğinizde güvenli kısımlar ile unsafe kısımlar karışık olmaz mı? O zaman elle yapılacak iş yalnızca unsafe bölgelerin güvenliğini doğrulamak olur gibi. En baştan Rust yazmaya benziyor
    Çıktının %90’ı unsafe değilse epey kazançlı olabilir gibi görünüyor

    • Aslında doğru. Birisi OpenJPEG’i c2rust ile düşük seviyeli unsafe Rust’a dönüştürmüştü
      OpenJPEG’in belirli bir test case’te segfault verdiği biliniyordu; o testi Rust sürümünde çalıştırınca aynı konuma karşılık gelen Rust kodunda segfault oluştu. En azından uyumlu olduğu söylenebilir
      Ama bu yaklaşım çıkmaz sokak. İlerleme kaydetmek için çeviricinin C’deki yaygın idiom’ları tanıyıp hedef dilin doğal biçimine yükseltmesi gerekir. Rust’a “compile” edince, güvenli olmayan C tarzı pointer manipülasyon fonksiyon çağrılarıyla dolu berbat bir Rust ortaya çıkar
      En büyük yükseltme problemi çoğunlukla pointer’larla ilgilidir. Bu makalenin en umut verici sonucu, C pointer aritmetiğini Rust slice’larına dönüştürmenin bir yolunu bulması. Slice’lar C pointer aritmetiğinin yaptığı işlerin çoğunu yapabilir ve artık biri bu çeviriyi otomatikleştirmiş durumda. Çevrilemeyen pointer aritmetiğine çok şüpheyle bakmak gerekir
      C’de bir diziyi gösteren raw pointer’ın örtük olarak bir uzunluk taşıdığını düşünmek faydalıdır. Bu uzunluk C kaynak kodunda görünmez ama program durumunun bir fonksiyonu olarak bir yerde vardır. Sabit olabilir, malloc istek boyutu olabilir, fonksiyon parametresi olabilir. Bakım yapan programcının dizi uzunluğunu bulması genelde çok zor değildir
      Bu, LLM’lere uygun bir problem olabilir. “Bu koda bakıp foo dizisinin uzunluğunun ne olduğunu bul” diye sorup, ardından LLM olmayan çeviricinin Rust dönüşümünü yönlendirmesi gibi. LLM yanılırsa Rust indeks hatası verir veya fazla büyük bir diziye sahip olur; ama güvensiz olmaz. C’deki dizi boyutu bilgisi idiom’ları yeterince kalıplaşmıştır, bu yüzden çoğu durumda doğru tahmin edilebilir. Özellikle LLM’ler yorumları da okuyabilir
    • Saf çeviri neredeyse tamamen unsafe Rust kodu üretir. Çünkü referanslar yerine her yerde raw pointer kullanmak zorunda kalır
      C kodu Rust’ın aliasing modeli ve borrow checker kısıtları düşünülerek yazılmadığından, onu referanslara çevirmek zordur
  • Yalnızca çok küçük bir C alt kümesini derliyor. Pratikte işe yaramayacak kadar küçük bile olabilir
    Bu tür yaklaşımlardan beklentim düşük. C kodunun statik analiziyle yapılabileceklerin sınırına mutlaka çarpacaktır. Üstelik hedef olarak Rust seçildiği için sorun gereksiz yere zorlaşmış. Rust’ın sahiplik modeli, gerçek C programlarının çalışma biçiminden çok farklı

    • Rust’ın sahiplik modeli C çevirisine yeterince yakın. Sadece daha açık ve güçlü tiplere sahip; bu yüzden çeviricinin serbest biçimli C kodunun ne yapmaya çalıştığını anlayıp Rust deyimlerine eşlemesi gerekiyor
      Örneğin C’deki bir tamponun doğal olarak bir uzunluğu vardır, ama C’de uzunluk işaretçiye açıkça bağlı değildir. Bu yüzden çeviricinin C programının uzunluğu nasıl takip ettiğini çıkarıp bunu slice’a dönüştürmesi gerekir. Uzunluk açık bir değişken olsa bile kolay değildir; hesaplanıyorsa ya da “sondan sonraki işaretçi” biçiminde ifade değiştiriyorsa daha da zorlaşır
      bool should_free_this_pointer gibi C kalıpları da Rust’ın Owned/Borrowed enum’una taşınabilir, ama hangi ayırmanın hangi boolean ile bağlantılı olduğunu ve ödünç alınmış varyantın gerçek güvenli kapsamının nerede olduğunu çıkarmak gerekir
    • Bir arayüz dili olarak iyi olabilir. Binding’ler için yararlı
    • Sonunda insanların işin içine LLM atıp, makul görünüp de büyük miktarda halüsinasyonla üretilmiş kodu sorun etmeyeceklerini söyleyeceklerini düşünüyorum
      Yine de rastgele C’den deyimsel Rust üretmenin zor olacağına katılıyorum. Yani “kabaca doğru” düzeyinde kalır
  • Bunun Zig’in C dönüştürme özelliğiyle nasıl karşılaştırıldığını merak ediyorum
    Zig, yeni kodun Zig’de, eski kodun C’de kaldığı karma bir ortam kurma, dönüştürme veya birlikte çalışabilirlik sağlama ve hatta C derleyicisi rolü üstlenme konusunda güçlü görünüyor
    Linux çekirdeği bakımcılarının Rust yerine Zig’i C’nin yerine geçecek bir seçenek olarak görmemelerinin çok iyi bir nedeni olmalı. Tahmin yürütecek kadar bilmiyorum; daha iyi bilenlerin açıklamasını isterim

    • Rust, “C’nin yerine geçen” bir şeyden çok C’ye eklenen bir araç. Torvalds ve diğerlerinin değerini kabul edip çekirdeğe izin verdiği bir araç; çekirdek kodunun büyük kısmı C ile yazılmaya devam edecek
      Çekirdek bakımcısı değilim ama Rust’ın Zig yerine seçilmesinin iki büyük nedenini tahmin edersem: dilin sunduğu derleme zamanı garantileri daha iyi olabilir ve benimsenme hızı da daha yüksek olabilir
      Sektördeki büyük şirketler API’ler için Rust native kodu veya bakımı yapılan Rust binding’leri sunmak üzere çok iş yapıyor. Windows geliştiricileri de kendi çekirdeklerinin bazı bölümlerini Rust ile yeniden yazıyor. Oldukça uzun süredir devam eden bir hareket var ve umarım durmaz
      Bakımcılar Zig’in C’ye kıyasla yeterli avantaj sunmadığını düşünüyor olabilir. Zaten birçoğu hâlâ Rust’a da karşı
    • Anladığım kadarıyla çoğu çekirdek bakımcısı C’yi herhangi bir şeyle değiştirmek istemiyor
      Zig’in C ile birlikte çalışabilirliği Rust’tan çok daha iyi, ama bellek güvenli değil ve kararlı hâle de gelmedi. C dünyasında Zig’in benimsenmesi epey artacaktır, ama Rust ile doğrudan rekabet ettiğini söylemek zor
      Benim bölgemde kimse Rust’ı benimsemiyor; C++ kullananlar C++’ta kalıyor. Başta Rust’a biraz ilgi vardı ama bildiğim hiçbir şirkette yer edinemedi. Go’nun genç şirketlerde ciddi büyüyüp geleneksel Java/C# şirketlerine pek girememesine benzer nedenler olabilir. Teknik olarak mantıklı olsa bile, devasa bir değişim yönetimi meselesi
      Zig, dinamik bellek ayırma gerektirmeyen programlar tarafında ivme kazanıyor, ama bunun ötesinde pek bir şey yok
    • Zig henüz çekirdek için değerlendirilecek kadar olgun değil
      Hâlâ düzenli olarak kırıcı değişiklikler oluyor; bu bugün Zig için iyi bir şey ama Linux gibi devasa ve uzun ömürlü bir kod tabanı için iyi değil. Derleyici hataları da çıkıyor
      Bunu, Zig’in yönünü genel olarak beğenen biri olarak söylüyorum
    • Zig henüz 1.0 değil ve hiçbir geriye dönük uyumluluk garantisi yok. Neredeyse hiçbir yerde kullanılmadı; bazı kısımları umut verici görünse de değerini henüz kanıtlamadı
    • Nedeni Zig’in bellek güvenli olmaması da olabilir
  • C2Rust gibi araçların bundan yararlanıp biçimsel olarak doğru kod üretebilip üretemeyeceğini merak ediyorum
    Ayrıca yazarların ne kadarını elle yaptığını, yoksa bir şey çalıştırıp Rust kodu üretip üretmediklerini de merak ediyorum. Öyleyse Rust üreten kodun nerede olduğunu bilmiyorum; kaynak depo bağlantısı da görünmüyor

    • Makalede, inceleme süreci bittikten sonra, yani genel olarak makale resmen yayımlandıktan sonra bu geliştirme çıktısını açık kaynak lisansıyla yayımlayacakları yazıyor
  • Bir C kütüphanesi çalışıyorsa, yani sorun olmadığı biçimsel olarak kanıtlanmış olmasa da çoğunlukla düzgün çalışıyorsa, neden unsafe Rust kullanarak çevrilmediğini merak ediyorum
    Rust’ta genel olarak kütüphane eksiği olduğu için bunun değerli olduğunu düşünüyorum. Sonuçta bazı durumlarda güvensiz olabilecek C ile yazılmış bir dll/so kullanmaktan pek farklı değil