- Ladybird, normal web içeriklerini bir ölçüde işleyebiliyor; ancak Google Project Zero’nun DOM fuzzer’ı Domato çalıştırılınca tarayıcı motorunun gizli uç durumları hızla ortaya çıktı
- JavaScript ile parser kurallarını aşarak oluşturulan DOM’lar, window’u olmayan belgeler ve döngüsel SVG referansları gibi gerçekçi biçimde mümkün olan anormal girdilerde 5 gerçek hata bulundu ve düzeltildi
- `` için table atası varsayımı,
DOMParser belgesinin window varsayımı ve Element.before() içinde kardeş düğüm arama hatası gibi uygulama içindeki örtük varsayımlar crash’lere veya sonsuz döngülere yol açtı
- Kaldırılmış bir iframe’in
contentWindow erişimi sorunu yalnızca Ladybird’e özgü bir kusur değildi; HTML standardındaki browsing context varsayımıyla da bağlantılıydı ve WHATWG HTML issue’suna dönüştü
- Domato gibi fuzzer’lar, normal web sayfası testleriyle yakalanması zor güvenlik ve kararlılık sorunlarını açığa çıkarır; Ladybird’ün sonraki hedefi ise sürekli fuzzing’e dayanacak kadar kararlı hale geldikten sonra bunu otomatik çalıştırmak
Domato ile Ladybird’ü stres testine sokmak
- Ladybird iyi yapılandırılmış web içeriklerini bir ölçüde işleyebiliyor; ancak güvenlik araştırma araçlarıyla anormal girdiler verildiğinde hangi sorunların ortaya çıkacağını kontrol ettik
- Kullanılan araç, Google Project Zero’nun DOM fuzzer’ı Domato
- Domato, çoğu geçerli ama tuhaf HTML, CSS ve JavaScript karışımı rastgele web sayfaları üretir
- Üretilen sayfalar Ladybird’ün debug build’ine yüklendi ve davranışları gözlemlendi
- Domato README’si büyük tarayıcılarda bulduğu çok sayıda hatayı öne çıkardığından, Ladybird’de de anlamlı kusurlar bulunabileceği düşünüldü
öğesi içindeyken null pointer dereference
- İlk sorun 1 saniyeden kısa sürede bulundu ve 562 KiB’lik Domato çıktısı aşağıdaki biçime indirgenebildi
let mfrac = document.createElement("mfrac");
mfrac.appendChild(document.createElement("th"));
document.body.appendChild(mfrac);
- UBSAN etkinleştirilmiş Ladybird build’inde
HTMLTableCellElement.cpp içindeki table_containing_cell çağrısı null pointer dereference’a neden oldu
- Nedeni, Ladybird’ün
ve uygulamasının DOM ağacında yukarıda her zaman `` bulunduğunu varsaymasıydı
- HTML parser, `` gibi markup’a izin vermez
- Standarda uyan tarayıcılar bu markup’ı yüklediğinde içi boş tek bir `` oluşturur
- Ancak JavaScript DOM API’siyle düğümler doğrudan oluşturulursa parser kurallarının bir kısmı aşılabilir ve
içine yerleştirilebilir
- Sorunlu kod,
ve için yalnızca table box’a değil her hücreye de CSS border ve padding uygulanmasını sağlayan eski davranışı uygulamakta kullanılıyordu
- Düzeltme,
ve öğelerinin her zaman `` atası olduğu varsayımını kaldırarak yapıldı
table_containing_cell(*this) yerine first_ancestor_of_type() kullanıldı
- Table atası yoksa hemen geri dönüyor
- Düzeltme commit’i burada
Window’u olmayan belgede `` event handler atama
- İkinci sorun da 1 saniye içinde bulundu ve 472 KiB’lik Domato çıktısı şu koda indirgendı
var parser = new DOMParser();
var doc = parser.parseFromString("", "text/html");
var body = doc.createElement("body");
body.onblur = null;
- Ladybird,
GCPtr doğrulama hatasıyla durdu
- Kilit nokta, `` öğesinin
onfoo event handler özniteliklerinin sahip olduğu özel davranıştı
- Eski web içerikleriyle uyumluluk için
document.body.onfoo ataması window.onfooya aktarılmalıdır
- Ancak
DOMParser ile oluşturulan belgelerde window nesnesi yoktur
- Ladybird’ün iç nesne modeli, tüm document’ların her zaman window’a sahip olduğu yönünde hatalı biçimde yapılandırılmıştı
- Düzeltmeden sonra
Document::window() nullable bir değer döndürüyor ve birçok yerde null işleniyor
- Window’u olmayan bir belgede
document.body.onblur atandığında diğer tarayıcılarda olduğu gibi hiçbir şey olmuyor
SVG `` içinde döngüsel referans
- Üçüncü sorun, bir SVG gradient’inin kendisine referans vermesiyle oluşan sonsuz recursion idi
- SVG, hem HTML içindeki inline SVG’yi hem de harici image formatını desteklemek zorundadır; gradient’ler renkleri miras almak için başka gradient’lere referans verebilir
- Ladybird uygulaması, bir gradient’in kendisine referans verdiği durumu hesaba katmadığı için referans zincirini izlerken sürekli döngüye giriyordu
- Yalnızca kendisine referans verme durumunu engellemek, birden fazla aşamadan oluşan döngüsel referansları ele alamaz
- Doğru işlem, ziyaret edilen tüm gradient’leri izlemek ve daha önce ziyaret edilmiş bir gradient ile tekrar karşılaşınca zincir takibini durdurmaktır
- Firefox, bu tür gradient’ler için geliştirici konsolunda şikâyette bulunur
Kaldırılmış iframe’in window özelliğine erişim ve HTML standardı hatası
- Dördüncü sorun, iframe kaldırıldıktan sonra önceden elde tutulan
contentWindow üzerinde getSelection() çağrıldığında oluştu
window.onload = function() {
let iframe = document.querySelector("iframe")
let iframeWindow = iframe.contentWindow;
iframe.remove();
iframeWindow.getSelection();
}
- Ladybird,
WindowProxy.cpp içinde BrowsingContext için null pointer reference binding runtime error verdi
- iframe DOM’dan kaldırıldığında, onun content document’ı kendi browsing context’inden ayrılır
- Window nesnesinin bir özelliği alındığında veya ayarlandığında HTML standardı algoritması
"check if an access between two browsing contexts should be reported" çalışır
- Bu algoritma, erişen window ile erişim hedefi window’un browsing context’ini inceler
- Standart, özellik erişimi anında iki window’un da bağlı bir browsing context’e sahip olduğunu hatalı biçimde varsayar
- HTML standardı için bir issue açıldı ve Ladybird’e öncelikle null check eklendi
- Ladybird üzerinde çalışırken bir standart hatası bulunursa, bug report veya düzeltme önerisiyle standardın herkes için iyileştirilmesi sağlanabilir
Element.before() içinde sonsuz döngü
- Beşinci sorun, sayfa yüklemesinin bitmemesi ve CPU’nun %100 kullanılması biçimindeydi
two.before(one);
- Neden,
before() uygulamasında `` öğesinin önceki kardeşleri arasında argümanlara dahil olmayan ilk kardeşi bulma mantığındaki hataydı
- Mevcut döngü her seferinde
node->previous_sibling() değerini yeniden alıyordu
while (auto previous_sibling = node->previous_sibling()) {
// check if previous_sibling is one of the arguments
}
- Aslında kardeş zinciri boyunca
previous_sibling->previous_sibling() ile ilerlemeliydi
for (auto sibling = node->previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
// check if previous_sibling is one of the arguments
}
Fuzzing sonuçları ve sonraki adımlar
- Bu oturumda 5 gerçek hata bulundu; bunlardan biri HTML standardı hatasıydı ve hepsi düzeltildi
- Tuhaf ve beklenmedik girdilerle karşılaşıldığında Ladybird’ün çok hızlı çöktüğü ortaya çıktı
- Domato gibi fuzzer’lar, yazılımını daha sağlam hale getirmek isteyenler için yararlı kaynaklardır
- Sonraki adım, Ladybird’ü sürekli fuzzing girdilerine dayanabilecek seviyeye kadar kararlı hale getirmek
- Yeterince kararlı hale geldiğinde, daha fazla sorun bulmak için bulutta bir yerlerde otomatik olarak çalıştırılması planlanıyor
1 yorum
Hacker News yorumları
Bir spesifikasyonun birden fazla bağımsız implementasyonunun neden değerli olduğunu çok iyi gösteriyor.
Sadece bu yazı bile spesifikasyondaki bir açığı ortaya çıkardı; daha fazlası da vardı ya da ileride çıkacak gibi görünüyor.
Web platformunun uzun vadeli sağlığı için birden fazla bağımsız implementasyon önemli, biz de o rolü üstlenmeye çalışıyoruz.
Mesela “kabak en sevdiğim sebze” diye tweet atıyorum, biri gelip “aslında meyve” diye düzeltiyor, sonra da bundan “Twitter’ın değeri kanıtlandı” sonucunu çıkarıyormuş gibi.
Bu çalışmanın kendisinin ya da bir spesifikasyonun birden fazla implementasyonunun değersiz olduğunu söylemiyorum, ama bu özel örnek tek başına henüz o çıkarımı desteklemiyor bence.
Bu projenin, küçük bir ekibin de şaşırtıcı şeyler yapabileceğini göstermeye devam etmesini seviyorum.
Çok sayıda paydaşı olan bir şirket içinde bunu başarmak muhtemelen çok daha zor olurdu.
Hobi projesiyse dönüp yeniden yapmak her zaman mümkün, ama bunların bazılarının en baştan mimariye işlenmiş olması gerekmiyor muydu hissini silmek zor.
SVG'yi şimdiden implemente ettiler mi? Beklediğimden çok daha hızlı ilerliyorlar, ilgiyle takip ediyorum.
Özellikle animasyon büyük bir eksik.
Issue #3 için, başka bir gradient’i işaret eden gradient’lere maksimum derinlik sınırı koymak da iyi bir fikir gibi görünüyor.
“Bu referansı daha önce gördük mü” mantığındaki hata ya da sınırlamalara karşı ek bir savunma katmanı olurdu.
SVG gradient’leri konusunda uzman değilim ama referans zincirlerinin 1000 halka kadar uzanması için meşru nedenler olabilir; yine de gerçek hayatta böyle bir şey görsem bunun saldırı ya da fuzzer girdisi olma ihtimalinin daha yüksek olduğunu düşünürdüm.
Bu yorumu Ladybird üzerinde yazıyorum.
Artık Hacker News, Ladybird’de çalışıyor.
Günde birkaç dakika Hacker News ya da OSnews gibi sitelere göz atarken Ladybird kullanıyorum.
Yavaş ve kırılgan ama çalışıyor. Projenin bu kadar genç olduğunu ve kelimenin tam anlamıyla her şeyin sıfırdan yazıldığını düşününce bu bile başlı başına etkileyici.
Ladybird’ün olgunlaşmasını gerçekten sabırsızlıkla bekliyorum.
İlginç, ama neredeyse tüm geliştiricilerin issue #1’de görüldüğü gibi “buldum! düzeltme commit’ini attım, bitti!” diye yaklaşması sinir bozucu.
Böyle olmamalı; tam olarak neyin yanlış gittiğini anlamak gerekir. Örneğin sorun “ebeveynin mutlaka var olduğu” varsayımıysa, kod tabanının tamamında aynı türden hataları aramak gerekir.
Aynı şeyin başka nerede olabileceğini bulmak için yaratıcı düşünmek gerekir. Asla sadece tek bir yerde olmaz.
Modern yazılımın güvenilmesi zor, hatalarla dolu bir kâbus olması büyük ölçüde kapitalist kısıtlar yüzünden, ama yine de daha iyisini yapabiliriz.
Ladybird’ün bu yılki Web Engines Hackfest’te yer alıp almayacağını merak ediyorum.
Konudan biraz farklı ama YouTube’daki hacking videolarına ne olduğunu merak ediyorum.
Eskiden yeni videoları beklerdim ama sanki bir süredir görmedim.
Aylık güncelleme videolarını hâlâ yüklüyorum ama son hacking videosunun üzerinden birkaç ay geçti.
Yine de Ladybird üzerinde her gün çalışıyorum ve geçen yıl Shopify ile başka yerlerden gelen cömert destek sayesinde artık iki tam zamanlı mühendisi de yönetiyorum.