Keyfi kod güvenliği için programlı olarak değerlendirmek mümkün müdür?


10

Son zamanlarda güvenli kod hakkında çok şey düşünüyorum. Konu güvenli. Bellek güvenli. Bir segfault kasası ile yüzünüzde patlamayacak. Ancak sorudaki netlik uğruna, Rust'un güvenlik modelini devam eden tanımımız olarak kullanalım.

İçin Rust ihtiyacına göre kanıtlanmış olarak, nedeni genellikle güvenliğinin sağlanması bir nasıl büyük-is-net sorununun bir parçasıdır, unsafe, eşzamanlılık gibi bazı çok makul programlama fikirleri vardır edemez kullanmadan Rust uygulanacak unsafeanahtar kelime . Eşzamanlılık kilitler, muteksler, kanallar ve bellek yalıtımı veya sizin neyinizle mükemmel bir şekilde güvenli hale getirilebilse de, bu Rust'un güvenlik modelinin dışında çalışmayı unsafeve derleyiciyi manuel olarak "Evet, ne yaptığımı biliyorum Bu güvensiz görünüyor, ancak matematiksel olarak mükemmel bir şekilde güvenli olduğunu kanıtladım. "

Ancak tipik olarak, bu şeylerin manüel olarak modellerini yapmak ve teorem kanıtlayıcılarıyla güvende olduklarını kanıtlamakla ilgilidir . Hem bilgisayar bilimi perspektifinden (mümkün mü) hem de pratiklik perspektifinden (evrenin hayatını alacak mı), keyfi bir dilde keyfi kod alan ve programın " Pas güvenli "?

Uyarılar :

  • Bu konuda kolay bir nokta, programın can sıkıcı olabileceğine ve bu nedenle durma sorununun bizi başarısızlığa uğratacağına işaret etmektir . Diyelim ki okuyucuya beslenen herhangi bir program durdurulabilir
  • Amaç "keyfi bir dilde keyfi kod" olmakla birlikte, elbette bunun, programın belirli bir dil olarak alacağımız seçilen dile aşina olduğuna bağlı olduğunu biliyorum.

2
Rasgele kod? Hayır . I / O ve donanım istisnaları nedeniyle en kullanışlı kodun güvenliğini bile kanıtlayamayacağınızı düşünüyorum .
Telastyn

7
Durma Problemini neden dikkate almıyorsunuz? Bahsettiğiniz örneklerin her birinin ve daha fazlasının Durdurma Sorunu, İşlev Sorunu, Pirinç Teoremi veya diğer birçok Kararsızlık Teoreminin çözümüne eşdeğer olduğu kanıtlanmıştır: işaretçi güvenliği, bellek güvenliği, iplik -güvenlik, istisna-emniyet, saflık, I / O-emniyet, kilit-emniyet, ilerleme garantileri, vb. Durdurma Sorunu muhtemelen bilmek isteyebileceğiniz en basit statik özelliklerden biridir, listelediğiniz her şey çok daha zordur .
Jörg W Mittag

3
Sadece yanlış pozitifleri önemsiyorsanız ve yanlış negatifleri kabul etmek istiyorsanız, her şeyi sınıflandıran bir algoritmaya sahibim: "Güvenli mi? Hayır"
Caleth

Eşzamanlı kod yazmak için kesinlikle Rust kullanmanıza gerek yokturunsafe . Senkronizasyon ilkellerinden aktörlerden ilham alan kanallara kadar çeşitli mekanizmalar mevcuttur.
RubberDuck

Yanıtlar:


8

Nihayetinde burada bahsettiğimiz şey derleme zamanı ve çalışma zamanı.

Derleme zamanı hataları, eğer bunu düşünürseniz, derleyicinin programınız çalıştırılmadan önce hangi problemleri belirleyebileceğini belirler. Açıkçası bir "keyfi dil" derleyicisi değil, ama kısa süre içinde buna geri döneceğim. Derleyici, tüm sonsuz bilgeliğinde, derleyici tarafından belirlenebilen her sorunu listelemez . Bu kısmen derleyicinin ne kadar iyi yazıldığına bağlıdır, ancak bunun temel nedeni çalışma zamanında birçok şeyin belirlenmesidir .

Çalışma zamanı hataları, iyi bildiğim gibi eminim, programın yürütülmesi sırasında meydana gelen herhangi bir hata türüdür. Buna sıfıra bölme, boş işaretçi istisnaları, donanım sorunları ve diğer birçok faktör dahildir.

Çalışma zamanı hatalarının doğası, derleme zamanında söz konusu hataları tahmin edemeyeceğiniz anlamına gelir. Eğer yapabilseydiniz, derleme zamanında neredeyse kesinlikle kontrol edilirlerdi. Bir sayının derleme zamanında sıfır olduğunu garanti edebiliyorsanız, herhangi bir sayıyı bu sayıya bölmek, sıfıra bölmekten kaynaklanan aritmetik bir hataya neden olur.

Bu nedenle, çok gerçek bir şekilde, bir programın düzgün işleyişini garanti etme düşmanı, zaman kontrollerini derlemek yerine çalışma zamanı kontrolleri gerçekleştirmektedir. Buna bir örnek, başka bir türe dinamik bir yayın yapmak olabilir. Buna izin verilirse, programcı olarak, derleyicinin bunun güvenli bir şey olup olmadığını bilme yeteneğini geçersiz kıldınız. Bazı programlama dilleri bunun kabul edilebilir olduğuna karar verirken, diğerleri en azından derleme zamanında sizi uyaracaktır.

Başka bir iyi örnek, null'ların dilin bir parçası olmasına izin vermek olabilir, çünkü null'lara izin verirseniz null işaretçi istisnaları olabilir. Bazı diller, açık bir şekilde bildirilmeyen değişkenlerin, hemen bir değer atanmadan bildirilecek boş değerleri tutabilmesini engelleyerek bu sorunu tamamen ortadan kaldırmıştır (örneğin Kotlin'i alın). Boş bir işaretçi istisnası çalışma zamanı hatasını ortadan kaldıramasanız da, dilin dinamik doğasını kaldırarak hata oluşmasını önleyebilirsiniz. Kotlin'de, elbette null değerleri tutma olasılığını zorlayabilirsiniz , ancak açıkça bu şekilde belirtmeniz gerektiği için bunun mecazi bir "alıcılar dikkat" olduğunu söylemeye gerek yok.

Kavramsal olarak her dilde hataları kontrol edebilecek bir derleyiciniz olabilir mi? Evet, ancak muhtemelen derlenmiş olan dili önceden vermeniz gereken tıknaz ve son derece dengesiz bir derleyici olacaktır. Ayrıca programınız hakkında pek çok şey bilmiyordu, artık belirli diller için derleyiciler, bahsettiğiniz gibi durdurma sorunu gibi belirli şeyleri biliyorlar. Anlaşıldığı üzere, bir program hakkında öğrenmek ilginç olabilecek birçok bilgi parçasının öğrenilmesi imkansızdır. Bu kanıtlanmıştır, bu yüzden yakında herhangi bir zamanda değişmesi muhtemel değildir.

Birincil noktanıza dönüyoruz. Yöntemler otomatik olarak iş parçacığı için güvenli değildir. Bunun pratik bir nedeni var, bu da iplikler kullanılmadığında bile diş güvenli yöntemlerin daha yavaş olmasıdır. Rust, yöntemleri varsayılan olarak iş parçacığı güvenli hale getirerek çalışma zamanı sorunlarını ortadan kaldırabileceklerine karar verir ve bu onların seçimidir. Gerçi bir bedeli var.

Bir programın doğruluğunu matematiksel olarak kanıtlamak mümkün olabilir, ancak dilde kelimenin tam anlamıyla sıfır çalışma zamanı özelliklerine sahip olacağınız uyarısı olacaktır. Bu dili okuyabilir ve sürpriz olmadan ne yaptığını bilirsiniz. Dil muhtemelen doğada çok matematiksel görünecektir ve bu muhtemelen tesadüf değildir. İkinci uyarı, programın kendisiyle ilgisi olmayan çalışma zamanı hatalarının hala gerçekleşmesidir. Bu nedenle, program üzerinde çalıştırıldığı bilgisayarda hakkında varsayımlar bir dizi varsayarak doğru kanıtlanabilir doğru ve tabii ki her zaman değişikliği, sakın yok zaten ve sık olur.


3

Tip sistemi, doğruluğun bazı yönlerinin otomatik olarak doğrulanabilir kanıtıdır. Örneğin, Rust'un tür sistemi, bir başvurunun başvurulan nesneyi geçmediğini veya başvurulan bir nesnenin başka bir iş parçacığı tarafından değiştirilmediğini kanıtlayabilir.

Ancak tip sistemleri oldukça kısıtlıdır:

  • Hızla karar verilebilirlik sorunlarıyla karşılaşırlar. Özellikle, tip sisteminin kendisi karar verilebilir olmalıdır, ancak birçok pratik tip sistemi yanlışlıkla Turing Complete (şablonlar nedeniyle C ++ ve özellikler nedeniyle Rust dahil) içerir. Ayrıca, doğruladıkları programın bazı özellikleri genel durumda, en ünlüsü bazı programın durup durmadığı (veya ayrıştığı) kararsız olabilir.

  • Ayrıca, tip sistemleri hızlı bir şekilde, ideal olarak doğrusal zamanda çalışmalıdır. Mümkün olan tüm ispatlar tip sisteminde yer almamalıdır. Örneğin, tüm program analizinden genellikle kaçınılır ve kanıtlar tekli modüllere veya fonksiyonlara dahil edilir.

Bu sınırlamalar nedeniyle, yazı sistemleri yalnızca kanıtlanması kolay olan oldukça zayıf özellikleri doğrulama eğilimindedir, örneğin bir işlevin doğru türde değerlerle çağrıldığı. Yine de bu, anlamlılığı önemli ölçüde sınırlandırdığından, geçici çözümlere ( interface{}Go, dynamicC #, ObjectJava, void*C'deki gibi) veya statik yazımı tamamen kapatan dilleri kullanmak yaygındır .

Doğruladığımız özellikler ne kadar güçlü olursa, dil genellikle o kadar az anlamlı olur. Rust yazdıysanız, derleyicinin görünüşte doğru kodu reddettiği bu “derleyici ile kavga” anlarını bileceksiniz, çünkü doğruluğunu kanıtlayamadı. Bazı durumlarda, doğruluğunu kanıtlayabileceğimize inansak bile Rust'daki belirli bir programı ifade etmek mümkün değildir . unsafeRust veya C # mekanizması Tür sisteminin sınırları kaçmak için izin verir. Bazı durumlarda, denetimlerin çalışma zamanına ertelenmesi başka bir seçenek olabilir - ancak bu, bazı geçersiz programları reddedemeyeceğimiz anlamına gelir. Bu bir tanım meselesi. Panik yapan bir Rust programı, tip sistemi söz konusu olduğunda güvenlidir, ancak bir programcı veya kullanıcı açısından zorunlu değildir.

Diller yazı sistemleri ile birlikte tasarlanmıştır. Varolan bir dile yeni bir tür sisteminin uygulanması nadirdir (ancak bkz. Örneğin MyPy, Flow veya TypeScript). Dil, tür sistemine uygun kod yazmayı kolaylaştırmaya çalışacaktır, örneğin tip ek açıklamaları sunarak veya kanıtlanması kolay kontrol akış yapıları ekleyerek. Farklı diller farklı çözümlerle sonuçlanabilir. Örneğin Java, finalRust'un mutdeğişken olmayanlarına benzer şekilde tam olarak bir kez atanan değişkenler kavramına sahiptir :

final int x;
if (...) { ... }
else     { ... }
doSomethingWith(x);

Java, tüm yolların değişkeni atayacağını veya değişkene erişilmeden önce işlevi sonlandırıp sonlandırmayacağını belirlemek için tür sistem kurallarına sahiptir. Buna karşılık, Rust bildirilen ancak tasarlanmamış değişkenlere sahip olmadan bu ispatı basitleştirir, ancak kontrol akışı ifadelerinden değerleri döndürmenizi sağlar:

let x = if ... { ... } else { ... };
do_something_with(x)

Bu, ödevi anlamaya çalışırken gerçekten küçük bir noktaya benziyor, ancak net kapsam belirleme, ömür boyu ilgili kanıtlar için son derece önemlidir.

Java'ya Rust tarzı bir sistem uygulasaydık, bundan çok daha büyük problemlerimiz olurdu: Java nesnelerine yaşam süreleri ile açıklama eklenmez, bu yüzden onlara &'static SomeClassveya gibi davranmamız gerekirdi Arc<dyn SomeClass>. Bu, sonuçta ortaya çıkan kanıtları zayıflatır. Java'nın tür düzeyinde değişmezlik kavramı da yoktur, bu nedenle türleri &ve &muttürleri ayırt edemeyiz . Herhangi bir nesneyi Hücre veya Muteks olarak ele almalıyız, ancak bu, Java'nın gerçekten sunduğundan daha güçlü garantiler alabilir (senkronize ve geçici olmadığı sürece bir Java alanını değiştirmek güvenli değildir). Son olarak, Rust'un Java tarzı uygulama devralma kavramı yoktur.

TL; DR: tip sistemler teoremdir. Ancak, kararlılık sorunları ve performans endişeleriyle sınırlıdırlar. Hedefin dil sözdizimi gerekli bilgileri sağlayamayabileceğinden ve semantikler uyumsuz olabileceğinden, yalnızca bir tür sistemi alıp farklı bir dile uygulayamazsınız.


3

Ne kadar güvenli?

Evet, böyle bir doğrulayıcı yazmak neredeyse mümkündür: programınız sabit UNSAFE'yi geri vermek zorundadır. Zamanın% 99'u haklı olacaksın

Çünkü güvenli bir Rust programı çalıştırsanız bile, birisi yürütme sırasında fişi çekebilir: bu nedenle programınız teorik olarak gerekmese bile durabilir.

Sunucunuz bir sığınakta bir faraday kafesinde çalışıyor olsa bile, bir komşu işlemi bir kürekçi istismarını yürütebilir ve sözde güvenli Rust programlarınızdan birinde biraz ters çevirebilir.

Söylemeye çalıştığım şey, yazılımınızın deterministik olmayan bir ortamda çalışacağı ve birçok dış faktörün yürütmeyi etkileyebileceğidir.

Şaka bir yana, otomatik doğrulama

Riskli programlama yapılarını (başlatılmamış değişkenler, arabellek taşmaları vb.) Tespit edebilen statik kod analizörleri zaten var . Bunlar, programınızın bir grafiğini oluşturarak ve kısıtlamaların yayılımını (türler, değer aralıkları, sıralama) analiz ederek çalışır.

Bu tür analizler bu arada bazı derleyiciler tarafından optimizasyon amacıyla yapılır.

Bir adım daha ileri gitmek ve aynı zamanda eşzamanlılığı analiz etmek ve çeşitli iş parçacıklarında, senkronizasyonda ve yarış koşullarında kısıtlamaların yayılması hakkında kesinti yapmak kesinlikle mümkündür. Bununla birlikte, çok hızlı bir şekilde, yürütme yolları ve bilinen kısıtlamaları çıplaklarına dillendirecek birçok bilinmeyen (I / O, OS zamanlaması, kullanıcı girişi, harici programların davranışı, kesintiler, vb.) minimum ve keyfi kod üzerinde herhangi bir yararlı otomatik sonuçlara varılmasını çok zor hale getirir.


1

Turing bunu 1936'da durma sorunu üzerine yazdığı makaleyle ele aldı. Sonuçlardan biri, zamanın% 100'ünün kodu analiz edebileceği ve durup durmayacağını doğru bir şekilde belirleyebileceği bir algoritma yazmanın imkansız olması, zamanın% 100'ünü doğru bir şekilde yapabilen bir algoritma yazmanın imkansız olmasıdır. tanımlamak istediğiniz ancak "güvenlik" de dahil olmak üzere kodun belirli bir özelliği olup olmadığını belirleyin.

Bununla birlikte, Turing'in sonucu, zamanın% 100'ünü (1) kesinlikle kodun güvenli olduğunu belirleyen, (2) kodun güvenli olmadığını kesinlikle belirleyen veya (3) antropomorfik olarak ellerini kaldıran ve söyleyen bir program olasılığını engellemez. "Heck, bilmiyorum." Rust'un derleyicisi, genel olarak, bu kategoride.


“Emin değilim” seçeneğiniz olduğu sürece, evet?
TheEnvironmentalist

1
Paket program, bir program analiz programını karıştırabilen bir program yazmak her zaman mümkün olmasıdır. Mükemmellik imkansızdır. Pratiklik mümkün olabilir.
NovaDenizen

1

Bir programın toplamı (durması garanti edilen bir programın teknik adı) ise, yeterli kaynak verildiğinde program üzerinde herhangi bir keyfi mülkiyetin kanıtlanması teorik olarak mümkündür. Programın girebileceği her olası durumu keşfedebilir ve bunlardan herhangi birinin mülkünüzü ihlal edip etmediğini kontrol edebilirsiniz. TLA + model kontrolü dili yerine tüm devletlerin bilgisayar yerine, potansiyel programı devletlerin setleri karşı özelliklerini denetlemek için küme kuramı kullanan, bu yaklaşımın bir varyantını kullanır.

Teknik olarak, herhangi bir pratik fiziksel donanımda yürütülen herhangi bir program ya toplamdır ya da yalnızca sınırlı miktarda depolama alanına sahip olmanız nedeniyle kanıtlanabilir bir döngüdür, bu nedenle bilgisayarın sınırlı sayıda durumu olabilir. (Herhangi bir pratik bilgisayar aslında sonlu durum makinesidir, Turing tamamlanmamıştır, ancak durum alanı o kadar büyüktür ki, tamamlandıklarını iddia etmek daha kolaydır).

Bu yaklaşımdaki sorun, programın depolama miktarına ve büyüklüğüne göre üstel bir karmaşıklığa sahip olması, onu algoritmaların çekirdeği dışında herhangi bir şey için kullanışsız hale getirmesi ve önemli kod tabanlarına bütünüyle uygulanmasının imkansız olmasıdır.

Dolayısıyla araştırmanın büyük çoğunluğu kanıtlara odaklanmıştır. Curry-Howard yazışmaları, doğruluk kanıtı ve tip sisteminin bir ve aynı şey olduğunu belirtmektedir, bu nedenle pratik araştırmaların çoğu tip sistemleri adı altındadır. Bu tartışma ile özellikle ilgili olan Coq ve Idriss, daha önce bahsettiğiniz Rust'a ek olarak. Coq, altta yatan mühendislik problemine diğer yönden yaklaşır. Coq dilinde rastgele kodun doğruluğunu kanıtlayan program, kanıtlanmış programı çalıştıran kod oluşturabilir. Idriss bu arada saf bir Haskell benzeri dilde rastgele kod kanıtlamak için bağımlı bir tip sistemi kullanır. Bu dillerin her ikisi de yazara uygulanabilir bir kanıt üretmenin zor problemlerini yazarak tür denetleyicisinin kanıtı kontrol etmeye odaklanmasını sağlar. Kanıtı kontrol etmek çok daha basit bir sorundur, ancak bu, dillerin çalışmasını çok daha zorlaştırır.

Kanıtları kolaylaştırmak için özel olarak tasarlanan bu diller, programın hangi bölümleriyle hangi durumun ilgili olduğunu kontrol etmek için saflığı kullanır. Pek çok ana dil için, sadece bir parça devletin programın bir kısmının kanıtıyla ilgisiz olduğunu kanıtlamak, yan etkilerin ve değişken değerlerin doğası nedeniyle karmaşık bir sorun olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.