- Ladybird सामान्य वेब कंटेंट को कुछ हद तक संभाल लेता है, लेकिन Google Project Zero के DOM fuzzer Domato को चलाते ही ब्राउज़र इंजन के छिपे edge cases जल्दी सामने आ गए
- JavaScript से parser rules को bypass करके बनाया गया DOM, window के बिना documents, और cyclic SVG references जैसे वास्तविक रूप से संभव असामान्य inputs में 5 वास्तविक bugs मिले और ठीक किए गए
- `` के table ancestor होने की धारणा,
DOMParser document के window होने की धारणा, और Element.before() में sibling traversal की गलती जैसी implementation के भीतर की implicit assumptions crashes या infinite loops तक ले गईं
- हटाए गए iframe के
contentWindow access की समस्या सिर्फ Ladybird की कमी नहीं थी, बल्कि HTML spec की browsing context धारणा से भी जुड़ी थी और WHATWG HTML issue तक पहुँची
- Domato जैसे fuzzers ऐसे security और stability issues उजागर करते हैं जिन्हें सिर्फ सामान्य web page testing से पकड़ना मुश्किल है, और Ladybird का अगला काम इसे इतना stable बनाना है कि यह continuous fuzzing सह सके, फिर इसे automated रूप से चलाया जा सके
Domato से Ladybird का stress test
- Ladybird अच्छी तरह बने web content को कुछ हद तक संभाल लेता है, लेकिन security research tool से असामान्य inputs देकर देखा गया कि कौन-सी समस्याएँ सामने आती हैं
- इस्तेमाल किया गया टूल Google Project Zero का DOM fuzzer Domato है
- Domato अधिकतर valid लेकिन अजीब HTML, CSS, JavaScript वाले random web pages बनाता है
- बने हुए pages को Ladybird की debug build में लोड करके उसका व्यवहार देखा गया
- Domato README में प्रमुख browsers में मिले कई bugs का ज़िक्र होने की वजह से, यह माना गया कि Ladybird में भी सार्थक खामियाँ मिल सकती हैं
के के अंदर होने पर null pointer dereference
- पहली समस्या एक सेकंड से भी कम समय में मिल गई, और 562KiB के Domato output को नीचे के रूप में छोटा किया जा सका
let mfrac = document.createElement("mfrac");
mfrac.appendChild(document.createElement("th"));
document.body.appendChild(mfrac);
- UBSAN चालू Ladybird build में
HTMLTableCellElement.cpp के table_containing_cell call से null pointer dereference हुआ
- कारण यह था कि Ladybird की
और implementation मानकर चल रही थी कि DOM tree के ऊपर हमेशा `` होगा
- HTML parser `` जैसे markup को अनुमति नहीं देता
- spec का पालन करने वाले browsers ऊपर वाला markup लोड करने पर अंदर से खाली `` एक बनाते हैं
- लेकिन JavaScript DOM API से nodes सीधे बनाने पर parser rules के कुछ हिस्सों को bypass करके
के अंदर डाला जा सकता है
- समस्या वाला code पुराने behavior को implement करने के लिए इस्तेमाल हो रहा था, जिसमें
और सिर्फ table boxes पर ही नहीं बल्कि हर cell पर CSS border और padding लागू करते हैं
- fix इस तरह किया गया कि
और के हमेशा `` ancestor होने की धारणा हटा दी गई
table_containing_cell(*this) की जगह first_ancestor_of_type() इस्तेमाल किया गया
- अगर कोई table ancestor नहीं है तो तुरंत return किया जाता है
- fix commit यहाँ है
window के बिना document में `` event handler assign करना
- दूसरी समस्या भी एक सेकंड के भीतर मिल गई, और 472KiB के Domato output को इस code तक घटाया गया
var parser = new DOMParser();
var doc = parser.parseFromString("", "text/html");
var body = doc.createElement("body");
body.onblur = null;
- Ladybird
GCPtr validation failure के साथ रुक गया
- मुख्य बात `` की
onfoo event handler properties के special behavior से जुड़ी थी
- पुराने web content compatibility के लिए
document.body.onfoo assignment को window.onfoo तक forward होना चाहिए
- लेकिन
DOMParser से बनाए गए documents में window object नहीं होता
- Ladybird का internal object model गलत तरह से बना था, जैसे हर document के पास हमेशा window होगा
- fix के बाद
Document::window() nullable value लौटाता है, और कई जगह null को handle किया गया
- window के बिना document में
document.body.onblur assign करने पर दूसरे browsers की तरह कुछ नहीं होता
SVG `` की cyclic reference
- तीसरी समस्या तब आई जब SVG gradient खुद को refer कर रहा था, जिससे infinite recursion हुई
- SVG को HTML के inline SVG और external image format दोनों का समर्थन करना होता है, और gradients दूसरे gradients को refer करके color inherit कर सकते हैं
- Ladybird implementation में gradient के खुद को refer करने की स्थिति को नहीं सोचा गया था, इसलिए reference chain को follow करते हुए यह लगातार loop करता रहा
- सिर्फ self-reference रोक देने से कई चरणों वाले cyclic references को handle नहीं किया जा सकता
- सही handling यह है कि visit किए गए सभी gradients को track किया जाए, और कोई gradient दोबारा मिले तो chain tracking रोक दी जाए
- Firefox इस तरह के gradients के लिए developer console में शिकायत दिखाता है
हटाए गए iframe की window property access और HTML spec bug
- चौथी समस्या तब हुई जब iframe हटाने के बाद पहले से पकड़े गए
contentWindow पर getSelection() call किया गया
window.onload = function() {
let iframe = document.querySelector("iframe")
let iframeWindow = iframe.contentWindow;
iframe.remove();
iframeWindow.getSelection();
}
- Ladybird ने
WindowProxy.cpp में BrowsingContext के null pointer reference binding runtime error दिया
- iframe के DOM से हटते ही उसका content document अपने browsing context से अलग हो जाता है
- window object की properties को get या set करते समय HTML spec algorithm
"check if an access between two browsing contexts should be reported" चलाया जाता है
- यह algorithm access करने वाली window और target window, दोनों के browsing context की जाँच करता है
- spec गलती से मान लेती है कि property access के समय दोनों windows के पास connected browsing context होगा
- HTML spec के लिए issue खोला गया, और फिलहाल Ladybird में null check जोड़ा गया
- Ladybird पर काम करते समय spec bug मिलें तो bug report या fix proposal के जरिए सभी के लिए spec बेहतर की जा सकती है
Element.before() का infinite loop
- पाँचवीं समस्या में page loading खत्म ही नहीं हो रही थी और CPU 100% इस्तेमाल हो रहा था
two.before(one);
- कारण
before() implementation में `` के previous siblings में से arguments में शामिल न होने वाले पहले sibling को खोजने वाली logic की गलती थी
- पुराना loop हर बार
node->previous_sibling() को फिर से ले रहा था
while (auto previous_sibling = node->previous_sibling()) {
// check if previous_sibling is one of the arguments
}
- असल में sibling chain के साथ आगे बढ़ते हुए
previous_sibling->previous_sibling() तक जाना चाहिए था
for (auto sibling = node->previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
// check if previous_sibling is one of the arguments
}
fuzzing के नतीजे और अगले कदम
- इस session में 5 वास्तविक bugs मिले, जिनमें से एक HTML spec bug था, और सभी को ठीक कर दिया गया
- यह साफ हुआ कि अजीब और अप्रत्याशित inputs मिलने पर Ladybird बहुत जल्दी टूट जाता है
- Domato जैसे fuzzers उन लोगों के लिए उपयोगी संसाधन हैं जो software को अधिक robust बनाना चाहते हैं
- अगला कदम Ladybird को इतना stable बनाना है कि वह continuous fuzzing inputs सह सके
- पर्याप्त stability मिलने के बाद इसे cloud में कहीं automated रूप से चलाकर और समस्याएँ खोजने की योजना है
1 टिप्पणियां
Hacker News की राय
यह अच्छी तरह दिखाता है कि किसी specification के कई स्वतंत्र implementations क्यों मूल्यवान होते हैं
सिर्फ इस लेख से ही specification में एक छेद मिला, और लगता है कि और भी रहे होंगे या आगे भी मिलेंगे
web platform की long-term health के लिए कई स्वतंत्र implementations महत्वपूर्ण हैं, इसलिए हम भी वही भूमिका निभाने की कोशिश कर रहे हैं
उदाहरण के लिए, अगर मैंने ट्वीट किया कि “बैंगन मेरी पसंदीदा सब्ज़ी है” और किसी ने तुरंत सुधार दिया कि “असल में वह फल है”, और फिर इसे “Twitter की value साबित हो गई” कहने जैसा है
इसका मतलब यह नहीं कि यह काम या specification के कई implementations मूल्यवान नहीं हैं, लेकिन मुझे लगता है कि इस खास उदाहरण से वह implication अभी साबित नहीं होती
अच्छा लगता है कि यह project लगातार दिखा रहा है कि छोटी team भी कमाल की चीज़ें बना सकती है
बहुत सारे stakeholders वाली company के अंदर ऐसा करना कहीं ज्यादा मुश्किल रहा होगा
hobby project हो तो कभी भी वापस जाकर दोबारा बना सकते हैं, लेकिन यह एहसास हटाना मुश्किल है कि इनमें से कुछ चीज़ें शुरू से architecture में होनी चाहिए थीं
क्या इन्होंने पहले ही SVG implement कर लिया है? progress उम्मीद से कहीं तेज़ है, इसलिए दिलचस्पी से देख रहा हूँ
खासकर animation एक बड़ा missing हिस्सा है
issue #3 के मामले में, किसी दूसरे gradient को refer करने वाले gradient पर maximum depth limit लगाना भी अच्छा लगता है
“क्या हमने यह reference पहले देखा है” logic की गलती या limit के खिलाफ यह defense-in-depth हो सकता है
SVG gradients के बारे में मुझे ज्यादा नहीं पता, शायद reference chain के 1000 तक जाने की कोई valid वजह हो, लेकिन real-world में ऐसा दिखे तो मुझे उसके attack या fuzzer input होने की संभावना ज्यादा लगती है
यह comment Ladybird में लिखा जा रहा है
अब Hacker News Ladybird में काम करता है
मैं दिन में कुछ मिनट Hacker News या OSnews जैसी sites browse करने के लिए Ladybird इस्तेमाल करता हूँ
यह slow और fragile है, लेकिन काम करता है। project इतना नया है और सचमुच सब कुछ scratch से लिखा गया है, यह सोचें तो सिर्फ यह भी बड़ी बात है
Ladybird के mature होने का मुझे सच में इंतज़ार है
दिलचस्प है, लेकिन खटकता है कि लगभग हर developer issue #1 में दिखने की तरह “मिल गया! fix commit कर दिया, खत्म!” कहकर रुक जाता है
ऐसा नहीं होना चाहिए; ठीक-ठीक क्या गलत था, यह समझना चाहिए। उदाहरण के लिए अगर “parent ज़रूर मौजूद होगा” वाली assumption समस्या थी, तो पूरे codebase में उसी तरह की गलतियाँ ढूँढनी चाहिए
creativity इस्तेमाल करके देखना चाहिए कि यही बात और कहाँ हो सकती है। यह कभी सिर्फ एक जगह नहीं होती
modern software के भरोसा करने लायक न होने वाले bug-filled nightmare होने की वजह ज्यादातर capitalist constraints हैं, लेकिन फिर भी हम बेहतर कर सकते हैं
सोच रहा हूँ कि इस साल Web Engines Hackfest में Ladybird दिखाई देगा या नहीं
थोड़ा अलग विषय है, लेकिन YouTube के hacking videos का क्या हुआ, यह सोच रहा हूँ
पहले नए videos का इंतज़ार रहता था, पर लगता है कुछ समय से नहीं देखे
monthly update videos अभी भी डालता हूँ, लेकिन आखिरी hacking video के बाद कई महीने हो गए हैं
फिर भी Ladybird पर रोज़ काम कर रहा हूँ, और पिछले साल Shopify और अन्य जगहों की उदार sponsorship की वजह से अब दो full-time engineers को भी manage कर रहा हूँ