- DARPA’nın TRACTOR programı, eski C kodlarını otomatik olarak Rust’a dönüştürerek C programlarındaki bellek güvenliği açığı kategorilerini ortadan kaldırmayı hedefleyen bir araştırma programıdır
- C ve C++’ın bellek güvenliği sorunları 20 yılı aşkın süredir ele alınsa da, yalnızca hata tespit araçlarının yeterli olmadığı yönünde bir anlayış oluştu
- Alternatif, mevcut kodu derleme zamanında güvenli olmayan programları reddeden güvenli programlama dillerine taşımaktır
- Dönüştürme sonucunun, deneyimli bir Rust geliştiricisinin yazdığı kodla aynı kalite ve stile sahip olması hedefleniyor; statik ve dinamik analiz ile büyük dil modeli tabanlı makine öğrenimi birleştiriliyor
- MIT Lincoln Laboratory test ve değerlendirmeyi üstleniyor; benchmark’lar, kilometre taşı projeleri ve araçlar herkese açık sayfada yayımlanıyor
TRACTOR’ın hedefi
- TRACTOR, “Translating All C to Rust” programıdır ve eski C kodlarını otomatik olarak Rust’a dönüştürmeyi amaçlar
- Güvenlik açısından, C programlarında bulunan bellek güvenliği güvenlik açıkları kategorisinin tamamını ortadan kaldırmaya çalışır
- Dönüştürülen kodun yalnızca çalışır durumda olması değil, deneyimli bir Rust geliştiricisinin ürettiği kodla aynı kalite ve stile sahip olması hedeflenir
Rust’a dönüştürme neden gerekli?
- C ve C++’ın bellek güvenliği sorunları, yazılım mühendisliği topluluğunun 20 yılı aşkın süredir ele aldığı bir meseledir
- Yalnızca hata tespit araçlarına dayanan yaklaşımla sorunu yeterince çözmenin zor olduğu konusunda bir uzlaşı oluştu
- Daha çok tercih edilen yaklaşım, derleme zamanında güvenli olmayan programları reddedebilen güvenli programlama dilleri kullanmaktır
- TRACTOR bu hedef dil olarak Rust’ı kullanır
Otomatik dönüştürmede kullanılan teknolojiler
- TRACTOR, eski C kodlarını Rust’a taşımak için çeşitli yazılım analiz teknikleri ile makine öğrenimi tekniklerini birleştirir
-
Analiz teknikleri
- Statik analiz ve dinamik analiz birlikte kullanılır
- Büyük dil modellerini içeren makine öğrenimi teknikleri de bu birleşime dahildir
- Bu birleşim, C kodunun yapısını ve davranışını anlamak ve Rust koduna otomatik dönüştürme için gereken temel teknik ekseni oluşturur
Test, değerlendirme ve açık kaynaklar
- MIT Lincoln Laboratory ekibi, araştırma programının test ve değerlendirmesini yürütür
- Benchmark’lar, kilometre taşı projeleri ve araçlar MIT Lincoln Laboratory TRACTOR sayfasında yayımlanır
- İlgili içerik olarak Eliminating Memory Safety Vulnerabilities Once and For All bağlantısı verilmiştir
1 yorum
Hacker News yorumları
Bu arada https://sam.gov/opp/1e45d648886b4e9ca91890285af77eb7/view de bakmaya değer
Bu etkinliğin amacının TRACTOR'ın teknik hedeflerini ve zorluklarını açıklamak, potansiyel teklif sahiplerinin sorularını yanıtlamak ve kendi araştırmalarının TRACTOR program hedefleriyle nasıl örtüşebileceğini değerlendirme fırsatı sunmak olduğu belirtiliyor
Bu gerçekten çok zor görünüyor. Özellikle deneyimli kişilerin yazdığı deyimsel Rust, C'ye hiç benzemiyor ve ilginç kodların çoğu C++ ile yazılmış durumda
Sonuçta bu, bir C programındaki tüm ayırmaların ömürlerini statik olarak belirlemekle aynı şey değil mi diye düşünüyorum. Kullanıcı tanımlı ayırıcılar veya kapalı kaynak kütüphaneler üzerinden yapılan ayırmalar da buna dahil. Bu alanda uzun süredir araştırmalar var ama büyük başarılar pek yok; C/C++ programları, referans sayımı gibi güvenlik önlemleri olmadan bile ayırma ömürlerini kullanıcının hangi düğmeye bastığına bağlayabiliyor. İyi bir fikir değil ama mümkün
Bir başka bariz sorun da analiz edilecek programın tanım gereği hatalı kod olması. Ömürler mantıklı olsaydı bellek güvenliği açıkları da olmazdı ve değiştirilmesine gerek kalmazdı. Statik olarak “ömürlerin nasıl olması gerektiğini” tespit etmeye çalışan araştırmalar genellikle analiz edilen kodun baştan doğru olduğunu varsayar. Ömürlerin belirlenemediği noktaları bulup geliştiriciden yardım isteyen bir program hedeflenebilir
Bununla birlikte DARPA'nın otomatik program çevirisini ya da Rust'a otomatik çeviriyi denediği ilk sefer bu değil[2]
[1]: https://www.urbandictionary.com/define.php?term=DARPA%20hard
[2]: https://c2rust.com/
Bu proje, bu dilleri fiilen aynı düğüm tipi kümesiyle destekleyen birleşik bir soyut sözdizimi ağacına sahip olduğu için epey güzel. 2014 yazında Livermore'da staj yaparken üzerinde çalışma fırsatım olmuştu
Açık kaynak da: http://rosecompiler.org/ROSE_HTML_Reference/index.html
Ödünç alma denetleyicisiyle uğraşmak bile zor geliyorsa, C'nin mümkün tüm program uzayını ve meşhur tanımsız davranışlarını da hesaba katarak ödünç alma denetleyicisinin onaylayacağı koda otomatik çevirmek çok daha zordur. Derleyici yazımının klasik sorunlarından biri, geçerli programların uzayının derlenebilir programların uzayından çok daha büyük olmasıdır
Kısa bir aramada c2rust gibi denemeler de olduğunu gördüm[1]. TRACTOR'ın bundan nasıl farklı olduğunu merak ediyorum
[1] https://github.com/immunant/c2rust
Kaynak programı vermek yerine o programın kılavuzunu vermek daha ilginç olurdu. Ancak kılavuzların programın tüm ince davranışlarını kapsaması nadirdir; bu yüzden en azından gerçek referans değerleri elde etmek için kaynak koda bakmak gerekmesi muhtemel
Önerilen bu girişimden bir süredir haberdardım; artık kamuya açılıyor olması heyecan verici. Çok iddialı bir öneri ve bu düzeyde bir iddianın DARPA’nın misyonuna uygun olduğunu düşünüyor, başarılı olmasını diliyorum.
Bu alanda Rust’ı savunan biri olarak, bu öneriyi ileri taşıyanların C’den Rust’a otomatik çeviri olasılığına dair beklentilerini düşürmeye çalıştım. Bana göre temel engel, C kaynak kodunun Rust kaynak koduna göre daha az bilgi içermesi. C kodunu Rust koduna çevirmek için birinin ya da bir şeyin eksik bilgiyi üretmesi gerekir. Bir görüntüyü çok büyütmenin, orijinal görüntüde yakalanmamış bilgi bitlerini hatasız biçimde üretememesiyle aynı nedenle, bu eksik bilgiyi kusursuz üretmenin imkânsız olduğu kolayca kanıtlanabilir. Sonuçta mevcut kaynak kodda eksik olan bilgiyi ekstrapole etmek, yani icat etmek gerekir. Doğru ekstrapolasyon muhakeme gerektirir ve özellikle gözetimsiz dil modelleri bunu büyük ölçekte yaptığında kaçınılmaz olarak hata üretecek bir süreçtir. Bu sorunu bir ölçüde hafifletebileceğini düşündüğüm bir çözüm önermişliğim var, ama ayrıntılarına girmeyeceğim.
Nihayetinde bu projenin bir ölçüde başarı elde edebileceğini düşünüyorum; ancak temkinli ve ölçülü beklentilerle ilerlenmeli. Aynı zamanda kamuya açık hiçbir sonuç çıkmama olasılığı da var, bu yüzden bu aşamada aşırı anlam yüklenmemesi gerektiğini söylemek isterim. Özellikle hükümet tekil bir varlık değildir; dolayısıyla bu proje C’ye yönelik topyekûn bir reddiye ya da Rust’a yönelik topyekûn bir kutsama olarak yorumlanmamalı. Her kurum bellek güvenliği teknolojilerini benimseme yönünü ve takvimini kendisi belirleyecektir. Örneğin NIST yalnızca Rust’ı değil, Ada SPARK’ı ve çeşitli güçlendirilmiş C/C++ lehçelerini de öneriyor.
https://cpp-rust-assisted-migration.gitlab.io/blog/
Gerçek Rust’a geçirme projeleri çoğu zaman bu şekilde işler. Fuzzer’lar da ek test verisi üreterek dal kapsamasını artırmaya yardımcı olabilir.
Şahsen “tüm dünyayı Rust ile baştan yazalım” zihniyetini sevmiyorum. Yine de bir projeyi yeni bir dile ya da platforma taşımaya çalışıyorsanız mekanik çeviri kötü bir yöntemdir.
Daha iyi bir mimari planlamak ve daha iyi bir yazılım sistemi tasarlamak için zaman ayırmalı, ardından parçaları tek tek değiştirmenin yolunu bulmalısınız. Havada şato kurarsanız asla yere inmez. Bu sistemde Rust kullanmaya karar verdiyseniz sorun yok, ama Rust’a uygun biçimde yazmalısınız. C’yi Rust’a geri taşımaya çalışmamalısınız.
Bence çok daha iyi ve olgun süreç, C’yi modern C’ye güncellemek ve CBMC gibi bir model denetleyiciyle bellek, kaynak ve tamsayı aritmetiği güvenliğini doğrulamaktır. Kod tabanını, bilgi birikimini ve geliştiricileri korurken, kademeli Rust yeniden yazımıyla aynı güvenliği elde edebilirsiniz.
Elle mimarisi tasarlanmış bir Rust sürümüyle değiştirmenin kesinlikle daha iyi bir çözüm olduğuna katılıyorum, ama maliyeti de daha yüksek. Burada hedeflenen şey, RLBox tarzında “neredeyse hiç çaba harcamadan güvenliği büyük ölçüde iyileştiren” bir ürün gibi görünüyor. Kaynak varsa tamamen elle yeniden yazmayın demek değil; ama hiç yapmamaktan iyidir.
Başta çözmeye çalıştığınız problemi ChatGPT’ye sorarsanız daha sık doğru yanıt verir, ama yine de genel olarak iyi değildir. Çıktıdaki en temel şeyleri bile çalışır hâle getirmek için hâlâ çok fazla düzeltme ve düşünme gerekir.
Bunun açıkça iyi bir fikir olması anlamında bir cazibe değil. Çeviriyle oluşan “kötü Rust”, arka planda bug gürültüsü olan C’ye kıyasla üzerinde çalışılması çok daha kötü olabilir. Sadık çeviri sayesinde o bug’lar da “kötü Rust”ta aynen bulunacaktır. C’de insanlar sorunlarla nasıl başa çıkacaklarını bir ölçüde biliyor; ama “kötü Rust”ta Rust insanları böyle şeylere alışık olmadığından gerçekten kötü olur.
Yine de birilerinin bunu tekrar tekrar temizleyip katlanılabilir hâle getirecek teknikler geliştirme olasılığı sıfır değil. Ayrıca projenin hedef dışı bir çıktısı olarak “x, y, z yüzünden katlanılabilir Rust’a çevrilemez” türünde linter geri bildirimi de ortaya çıkabilir. C geliştiricileri bunu inceleyebilir; kod iyi Rust’a çevrilebilir hâle geldiyse, çevirmeye gerek kalması da azalır.
Böyle bir sonuç çıkarsa, bazıları için çözümü çalıştırılabilir C ile tarif edip “çevirici/linter”ın onları bozulmamış bir yaklaşıma yönlendirmesi daha kolay olabilir. Bu olumlu sonuçların olasılığını epey düşük görüyorum, ama ara sıra karanlık at bahsi yapmak DARPA’nın iş tanımına yakın değil mi diye düşünüyorum.
Asıl mesele, gerçek hayatta defalarca sorun olduğu kanıtlanmış bellek güvenliği CVE’lerini azaltmaya başlamak.
C/C++’tan Rust’a otomatik ve güvenilir biçimde çeviri yapılabilseydi bunun çoktan yapılmış olacağına katılıyorum. Ancak daha iyi mimari ve sistem planlayıp parçaları tek tek değiştirme sürecinin bir noktasında insanlar “Artık C’yi çok daha iyi kullanıyoruz, bellek güvenliği bug’ları da yapmıyoruz; burada durabiliriz, değil mi?” özgüvenine kapılabilir ve gerçekten de öyle yaparlar. Sonra 6 ay sonra yine trajikomik bir buffer overflow CVE’si çıkar.
C’yi modern C’ye güncellemek ve CBMC ile doğrulamak da muazzam bir yatırımdır. O kadarını yapacaksanız, bence Rust’a geçmek daha iyi. Kademeli Rust yeniden yazımıyla aynı güvenliği elde edeceğiniz sözü mümkün olabilir, ama oldukça belirsiz ya da net bir sonuçtan uzak görünüyor.
Pek çok kişi bunu tüm C ve C++ kodlarını Rust’a çevirme talebi ya da emri olarak okuyor, ama dikkat çekici proje adına rağmen özet öyle okunmuyor. Birbiriyle ilişkili ama ayrı iki paragraf var
Birincisi, C ve C++ büyük ölçekte yeterince güvenli değil. Dikkatli programlama ve iyi araçlar kullanılsa bile, temelde güvenli olmayan tasarımları yüzünden çok fazla güvenlik açığı ortaya çıkıyor. Bu nedenle mümkün olduğunca çok kodun “güvenli” dillere, özellikle bellek güvenliğini garanti eden dillere çevrilmesi ya da yeniden yazılması gerekiyor
İkincisi, mevcut C kodunu Rust’a çeviren yazılımların finanse edilip yarışmaya açılacağı
Bu, dünyayı Rust ile yeniden yazma mutabakatı değil. Güvenli dillere geçiş mutabakatı; Rust bunun örneklerinden biri ve bu geçişte Rust’ı hedefleyen bir program olduğu anlamına geliyor
unsafesöz dizimi varsa, onu kullanma kuralları ne olacak? Burada kullanılacak tanımlı bir kurallar kümesi yoksa, sonunda yine başlangıç noktasına dönülürRust’ın güvenli modu var. Rust’ın kendisi güvenli bir dil değil. İlginç işler yapmak için
unsafeblokları gerekiyor. Bununla çok şey kazanılmıyorÖte yandan programcının pointer’lara dokunmasına bile izin vermeyen çok sayıda garbage-collected dil var. Neden bu diller dikkate alınmıyor? Sebep performans. Rust programcıları performansı bu kadar “önemsediği” için, o dille temel problem asla çözülemez
Performans mı istiyorsunuz, güvenlik mi? İkisine birden sahip olamazsınız
Bunun herhangi bir otomasyon yöntemiyle çalışabilmesi gerçekten şaşırtıcı. Sıradan bir C programını Rust’a satır satır aktarmak mümkün değil
C programlarında pointer’lar ve aliasing her yerdedir; Rust ise bu kavramları açıkça engeller. Tamamını
unsafeile sarmalamadıkça, bir C programını Rust’ta yeniden yazmak için pek çok tipik yapıyı daha yüksek seviyede yeniden düşünmek gerekirHer şeyi tek seferde çevirmek de gerekmez. Rust derleyicisinin değerlerinden biri, tekrar LLM’e verilip yineleme yapılabilecek çok sayıda somut bilgi sağlamasıdır
Benim startup’ım grit.io’da benzer bir problem üzerinde çalışıyoruz ve C -> Rust’ın yakın vadede yeterince ele alınabilir olduğunu düşünüyorum. Kesinlikle kolay değil ama çözülebilir bir problem
Öyleyse bunu bir alt problem olarak ele alıp, verinin thread’ler arasında nasıl paylaşıldığını tespit etmeyi de başka bir problem yapabiliriz. İkinci durumda birçok programda “thread T1, kuyruk Q’ya koyar; thread T2 alır” gibi örtük sahiplik kuralları olacaktır. Bu, “kuyruğa koyunca sahiplik devredilir” şeklinde çevrilebilir
Bu tür kuralları tespit etmek kolay olmayacaktır, ama tamamen erişilemez bir iş gibi de görünmüyor; bir araştırma projesi olarak yeterince anlamlı olabilir
unsafeile sarmalayarak, aynı binary’ye derleyip, eşdeğerliği sürekli doğrularkenunsafekısımlarını yavaş yavaş çıkaramaz mıyız?Böylece en azından mümkün olduğunca çok kısmı Rust’a taşımış, mühendislerin yeniden düşünmesi gereken kavramları da ayrı ele almış oluruz gibi geliyor
unsafeolan bir Rust çevirisi yapılabilir gibiAI’ın gerçekten işe yarar ve geçerli bir program üretebilmesi için lifetime’ları vb. anlaması gerektiğini varsayıyorum. Gerçekten bunu yaparsa etkileyici olur
Çok sıkı kodlama pratiklerine uyup, koda özel açıklama notları yığmayı gerektiren üçüncü taraf statik doğrulama araçlarını entegre ederseniz C/C++ ile de benzer sonuçlar elde edilebilir
Ya da sadece Rust kullanırsınız
Gerçek bir alternatif varken, bu kadar büyük ve bariz tasarım kusurları olan bir dili aklı başında kim savunur ki
Bunun iyi gideceğini sanmıyorum. C’de Rust’ta büyük değişiklikler olmadan kopyalanamayacak soyutlamalar var
C’de aynı pointer’ı içeren iki farklı veri yapısının o pointer’a yazması yaygındır. Bu Rust’ta basitçe kopyalanamaz ve epey akıllıca müdahale gerektirir
Ama daha az hatalı bir çıktı ya da Rust’ın güvenilirliğini yaratan program yapısını otomatik üreten bir süreç elde edilmeyecek
Bunun otomatik olması mı gerekiyor? Öyleyse C’yi Rust’a otomatik port edebilen bir programın kaçınılmaz olarak C kodunun kendisini güvenli kılacak tüm işlevleri de içermesi gerekmez mi?
Bazı C kodları yalnızca “doğruluğu kanıtlanamaz” değil, “bellek güvenliği açısından fiilen yanlıştır”. Böyle kodlar insan müdahalesi olmadan otomatik çevrilmeyecektir. Doğru eşdeğer kod yoksa bu nasıl mümkün olsun? Aracın “Bu kodun ne yapmaya çalıştığını bilmiyorum; gerçekten yaptığı şeyin derleyiciyle yapılan anlaşmayı bozmak olduğunu ve dolayısıyla amaçlanan şey olmadığını da biliyorum. İnsan, yardım et” diyebileceği bir kaçış yoluna ihtiyacı var
Teorik olarak bu kaçış yolunun yalnızca tanımsız davranış gerçekten ortaya çıktığında kullanılmasını sağlamak imkânsızdır. Rice teoremi nedeniyle. Pratikte de aşırı karmaşık kodu körlemesine çevirmemek gerekir; bu yüzden bunu yapmaya çalışmanın kendisi büyük olasılıkla arzu edilir değildir
Sonuçta aracın işin çoğunu yapan ama insanın yönlendirdiği etkileşimli bir araç gibi olacağını hayal ediyorum. Ve o insan yönlendirmesinin sonucu olarak tam olarak eşdeğer kod değil, aynı amacı yerine getiren kod ortaya çıkacaktır
Eğer 1) Rust’ta bellek hatası yoksa ve 2) C otomatik olarak Rust’a çevrilebiliyorsa, bu tüm bellek hatalarının otomatik olarak düzeltilebileceği anlamına gelir
Bu neredeyse kesinlikle doğru değil. Bu işin genel durumda tamamen imkânsız olma olasılığı çok yüksek
Örneğin tüm dinamik bellek ayırmalarını çeviri zamanında belirlenen sabit tamponlarla değiştirip, bu tamponlara sığmayan tüm girdileri reddetmek yeterli olur