Program doğrulama teknikleri Heartbleed türünün hatalarının oluşmasını engelleyebilir mi?


9

Heartbleed böceğiyle ilgili olarak, Bruce Schneier 15 Nisan Kripto Gramında şöyle yazdı: “Felaket” doğru kelimedir. 1 ila 10 ölçeğinde, bu 11'dir. ' Birkaç yıl önce, belirli bir işletim sisteminin çekirdeğinin modern bir program doğrulama sistemi ile titizlikle doğrulandığını okudum. Bu nedenle, Heartbleed türünün hatalarının bugün program doğrulama tekniklerinin uygulanmasıyla meydana gelmesi engellenebilir mi, yoksa bu henüz gerçekçi değil, hatta prensip olarak imkansız mı?


2
İşte bu sorunun J. Regehr tarafından ilginç bir analizi.
Martin Berger

Yanıtlar:


6

Sorunuzu en kısa şekilde cevaplamak için - evet, bu hata potansiyel olarak resmi doğrulama araçları tarafından yakalanmış olabilir. Gerçekten de, "asla gönderilen kalp atışının boyutundan daha büyük bir blok göndermeyin" özelliğinin çoğu spesifikasyon dilinde (örn. LTL) resmileştirilmesi oldukça basittir.

Sorun (biçimsel yöntemlere karşı ortak bir eleştiri), kullandığınız özelliklerin insanlar tarafından yazılmasıdır. Aslında, resmi yöntemler sadece böcek avı mücadelesini hataları bulmaktan böceklerin ne olduğunu tanımlamaya kaydırır. Bu zor bir iş.

Ayrıca, devlet patlaması sorunu nedeniyle yazılımı resmi olarak doğrulamak zordur. Bu durumda, özellikle önemlidir, çünkü devlet patlamasından kaçınmak için birçok kez sınırları soyutlarız. Örneğin, "her talebin ardından 100000 adımda bir hibe gelir" demek istediğimizde, çok uzun bir formüle ihtiyacımız vardır, bu yüzden bunu "her talebin sonunda bir hibe takip eder" formülüne özetleriz.

Böylece, kalpten gelen durumda, gereksinimleri resmileştirmeye çalışırken bile, söz konusu sınır soyutlanmış olabilir ve aynı davranışa neden olabilir.

Özetlemek gerekirse, potansiyel olarak bu hata resmi yöntemler kullanılarak önlenebilirdi, ancak bu özelliği önceden belirten bir insan olması gerekirdi.


5

Klocwork veya Coverity gibi ticari program denetleyicileri, kontrol etmek için tasarlandıkları temel sorunlardan biri olan nispeten basit bir "sınır kontrolü hatası yapmayı unuttum" olduğundan Heartbleed'i bulabilirdi. Ancak çok daha basit bir yol var: arabellek taşması olmadığından iyi test edilmiş opak soyut veri türlerini kullanın.

C programlaması için bir dizi "güvenli dize" soyut veri türü vardır. En çok tanıdığım kişi Vstr . Yazar, James Antill, neden kendi kurucuları / fabrika yöntemleri ile bir dize soyut veri türüne ve ayrıca C için diğer dize soyut veri türlerinin bir listesine ihtiyaç duyduğunuz konusunda harika bir tartışmaya sahiptir .


2
Coverity Heartbleed'i bulamıyor, John Regehr'un analizine bakın .
Martin Berger

Güzel bağlantı! Hikayenin gerçek ahlakını gösterir: program doğrulaması kötü tasarlanmış (veya var olmayan) soyutlamaları telafi edemez.
Gezici Mantık

2
Program doğrulaması ile ne demek istediğinize bağlıdır. Statik analiz demek istiyorsanız, o zaman evet, Rice teoreminin doğrudan bir sonucu olarak bu her zaman bir yaklaşımdır. Etkileşimli bir teorem yordamında tam davranışı doğrularsanız, programın özelliklerini karşıladığı için bir dökme demir garantisi alırsınız, ancak bu son derece zahmetlidir. Ve yine de özelliklerinizin yanlış olabileceği sorunuyla karşı karşıyasınız (bkz. Örneğin Ariane 5 patlaması).
Martin Berger

1
@ MartinBerger: Coverity şimdi bulur .
Monica'yı eski

4

Bir " program doğrulama tekniği  " olarak sayılırsanız  , çalışma zamanı sınırlama ve bulanıklaştırma birleşimi varsa, evet bu özel hata yakalanmış olabilir .

Düzgün tüylenme, şimdi rezil olanın ait olduğu memcpy(bp, pl, payload);bellek bloğunun sınırını okumasına neden olacaktır pl. Çalışma zamanı sınır denetimi ilke olarak bu tür herhangi bir erişimi yakalayabilir ve pratikte bu özel durumda, işin yapıldığını mallocparametrelerini kontrol etmek isteyen bir hata ayıklama sürümü bile memcpy(burada MMU ile uğraşmaya gerek yoktur) . Sorun, her tür ağ paketi üzerinde bulanık testler yapmak çaba gerektiriyor.


1
Genel olarak doğru olsa da, IIRC, OpenSSL'nin davasında yazarlar, kendi dahili bellek yönetimini uyguladılar, böylece memcpybaşlangıçta sistemden istenen (büyük) bölgenin gerçek sınırına ulaşma olasılığı daha azdı malloc.
William Price

Evet, hata zamanında olduğu gibi OpenSSL söz konusu olduğunda , sistemi değil memcpy(bp, pl, payload)OpenSSL'nin mallocdeğiştirilmesi tarafından kullanılan sınırları kontrol etmek zorunda kalacaktı malloc. Bu, ikili düzeyde otomatik sınır denetimini dışlar (en azından mallocdeğiştirme konusunda derin bilgi sahibi olmadan ). Örneğin, jetonu değiştiren C makroları mallocveya kullanılan herhangi bir OpenSSL yedeği kullanılarak kaynak düzeyi sihirbazıyla yeniden derleme yapılmalıdır ; ve memcpyçok zeki MMU hileleri dışında aynı şeye ihtiyacımız var gibi görünüyor .
fgrieu

4

Daha sıkı bir dil kullanmak, sadece kale direğini uygulamanın doğru hale getirilmesinden spesifikasyonu doğru hale getirmeye taşımakla kalmaz. Çok yanlış ama mantıksal olarak tutarlı bir şey yapmak zordur; bu yüzden derleyiciler çok fazla hata yakalar.

İşaretçi Aritmetiği normalde formüle edildiği için sağlam değildir, çünkü tip sistemi aslında ne anlama geldiği anlamına gelmez. Çöp toplanan bir dilde (soyutlama için de ödeme yapmanızı sağlayan normal yaklaşım) çalışarak bu sorunu tamamen önleyebilirsiniz. Veya ne tür işaretçiler kullandığınız konusunda çok daha spesifik olabilirsiniz, böylece derleyici tutarsız olan veya yazıldığı gibi doğrulanamayan herhangi bir şeyi reddedebilir. Rust gibi bazı dillerin yaklaşımı budur.

Yapılandırılmış türler provalara eşdeğerdir, bu yüzden bunu unutan bir tür sistemi yazarsanız, her şey ters gider. Bir süre için bir tür beyan ettiğimizde, aslında değişkente ne olduğu hakkında gerçeği iddia ettiğimizi varsayalım.

  • int * x; // Yanlış bir iddia. x var ve bir int'i göstermiyor
  • int * y = z; // Yalnızca z'nin bir int'i gösterdiği kanıtlanırsa doğrudur
  • * (x + 3) = 5; // Yalnızca (x + 3) x ile aynı dizideki bir int'i gösteriyorsa doğrudur
  • int c = a / b; // Yalnızca b sıfırdan farklı olduğunda doğrudur, örneğin: "nonzero int b = ...;"
  • sıfırlanabilir int * z = NULL; // nullable int *, int * ile aynı değildir
  • int d = * z; // Yanlış bir iddia, çünkü z boş bırakılabilir
  • eğer (z! = NULL) {int * e = z; } // Tamam çünkü z boş değil
  • serbest (y); int w = * y; // Yanlış iddia, çünkü y artık w'de yok

Bu dünyada işaretçiler boş olamaz. NullPointer dereferences mevcut değildir ve işaretçilerin hiçbir yerde boş olup olmadıklarını kontrol etmeleri gerekmez. Bunun yerine "nullable int *", değeri null veya bir işaretçiye çıkarılabilen farklı bir türdür. Bu, null olmayan varsayımın başladığı noktada , istisnanızı günlüğe kaydedeceğiniz veya null bir dalı geçireceğiniz anlamına gelir.

Bu dünyada, sınırların dışında dizi hataları da mevcut değildir. Derleyici sınırda olduğunu kanıtlayamazsa, derleyicinin bunu kanıtlayabilmesi için yeniden yazmaya çalışın. Eğer yapamazsa, o noktaya manuel olarak bir Varsayım koymanız gerekir; derleyici daha sonra bir çelişki bulabilir.

Ayrıca, başlatılmamış bir işaretçiniz yoksa, başlatılmamış belleğe yönelik işaretçileriniz olmaz. Belleği boşaltmak için bir işaretçiniz varsa, derleyici tarafından reddedilmelidir. Rust'ta, bu tür kanıtları beklemeyi makul kılmak için farklı işaretçi türleri vardır. Sadece sahip olunan işaretçiler (diğer ad yok), derin değişmez yapılara işaretçiler vardır. Varsayılan depolama türü değiştirilemez vb.

Girdi yüzey alanını tam olarak tahmin edilenle sınırlamak için protokoller (arayüz üyeleri içeren) üzerinde iyi tanımlanmış gerçek bir gramer uygulanması da vardır. "Doğruluk" ile ilgili olan şey: 1) Tüm tanımlanmamış durumlardan kurtulun 2) Mantıksal tutarlılığı sağlayın . Oraya ulaşmanın zorluğu, son derece kötü aletler kullanmakla (doğruluk açısından) çok şey ifade ediyor.

Bu yüzden en kötü iki uygulama küresel değişkenler ve gotolardır. Bu şeyler, her şeyin önüne / post / değişmez koşullarının konulmasını engeller. Ayrıca türlerin bu kadar etkili olmasının nedeni de budur. Türler güçlendikçe (nihayetinde gerçek değeri hesaba katmak için Bağımlı Türleri kullanarak), kendi içinde yapıcı doğruluk kanıtı olmaya yaklaşırlar; tutarsız programlar yapmak derleme başarısız.

Bunun sadece aptalca hatalarla ilgili olmadığını unutmayın. Ayrıca, kod tabanını akıllı sızıntılardan korumakla da ilgilidir. "Resmi olarak belirtilen protokolü izler" gibi önemli özelliklerin inandırıcı bir makine tarafından üretilen kanıtı olmadan bir sunumu reddetmeniz gereken durumlar olacaktır.



1

otomatik / resmi yazılım doğrulaması yararlıdır ve bazı durumlarda yardımcı olabilir, ancak diğerlerinin de belirttiği gibi, gümüş bir kurşun değildir. OpenSSL'nin açık kaynağında savunmasız olduğu ve yine de ticari ve endüstri çapında kullanıldığı, yaygın olarak kullanıldığı ve yayınlanmadan önce yoğun bir şekilde gözden geçirilmediği (yani projede herhangi bir ücretli geliştirici olsa bile harikalar) söz konusu olabilir. kusur, temelde kodun yayınlanmasından sonra incelenmiştir ve kodun görünüşe göre yayın öncesi gözden geçirilmiştir (muhtemelen dahili kod incelemesini kimin yaptığını izlemenin bir yolu olmadığına dikkat edin). heartbleed (diğerleri arasında) ile "öğretilebilir an" temelde daha iyi kod inceleme, muhtemelen daha iyi izlenen, son derece hassas kod esp serbest bırakmadan önce idealdir. belki OpenSSL artık daha fazla incelemeye tabi olacak.

medyadan kökenlerini detaylandıran daha fazla bkg:

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.