隐藏画质代码_优秀的模糊测试代码是如何炼成的?
所謂模糊測試,是指一種通過向目標系統提供非預期的輸入并監視異常結果來發現軟件漏洞的方法,它經過了近 20 年的發展,早已在程序員圈中成為一種主流漏洞挖掘技術。基于此,開發者們該如何編寫良好的模糊測試代碼?
作者 |?John Regehr
譯者 | 彎月,責編 | 屠敏
出品 | CSDN(ID:CSDNnews)
以下為譯文:
模糊測試(Fuzzing)是發掘漏洞和其他軟件缺陷的強力手段,但通常開發者都是利用這種方式來深入發掘已部署代碼中的問題。實際上,我們應該盡早完成模糊測試,而且開發人員應該花點時間來編寫更易于開展模糊測試的代碼。
在這篇文章中,我想介紹一些方法,告訴你如何編寫更方便模糊測試的代碼。但這些方法并不全面,而且各個方法之間也可能有重疊。縱觀本文,我會使用“模糊器”來指代所有類型的隨機測試用例生成器,無論是基于突變的(afl、libFuzzer等)還是生成的(jsfunfuzz、Csmith等)生成器。注意,本文提出的建議并非適用所有情況,但其中很多都是合理的軟件工程建議。我會用粗體標明一些我認為特別重要的觀點。
在測試預言上下功夫
測試預言(test oracle)決定了測試用例是否會觸發bug。在默認情況下,afl等模糊器唯一可以使用的預言就是操作系統的頁面保護機制。換句話說,它只能檢測系統的崩潰。但是我們能做到遠不止于此。
斷言和由編譯器插入的sanitizer檢查是另一種類型的預言。在模糊測試中,你應該盡可能多地使用這種類型的檢查。除了這些簡單的預言之外,還有很多其他的預言,例如:
函數與反函數:解析與輸出的閉環、壓縮與解壓縮的閉環、加密與解密的閉環及其他類似的代碼是否會按預期正常工作?
差異:兩個不同的實現或同一個實現的兩種不同的模式是否表現出相同的行為?
變形:如果在保證語義的情況下修改測試用例,系統是否會表現出的行為,例如在表達式中再添加一層括號?
資源:處理輸入時,系統消耗的時間、內存等是否合理?
特定領域:例如,一個因壓縮而導致畫質受損的圖像在視覺上是否與未壓縮的圖像一致??
我們值得花時間和精力建立強大的預言,因為它們往往能夠發現應用程序級的邏輯錯誤,而通常我們通過查找數組越界等方式只能捕獲低級的錯誤。
幾年前,我撰寫過一篇有關于該話題的文章(https://blog.regehr.org/archives/856)。有一個Twitter用戶建議:“在測試解析器的時候,你必須檢查它返回的對象,而不僅僅是檢查解析這個動作的執行。”這是一個很好的建議。
干預I/O與狀態
無狀態代碼更方便展開模糊測試。但除此之外,你還需要API來控制狀態和干預I/O。例如,如果程序需要從操作系統中獲取核心數、當前日期或剩余磁盤空間量等信息,那么你應該提供一種設置這些值的方法并寫在文檔中。我并不是說,我們需要隨時改變核心數量,而是我們可能在單核模式下針對代碼展開模糊測試,然后還需要在128核模式下再進行一次模糊測試。有些控制狀態和I/O的方法非常重要,比如簡化重置狀態(為了支持持久模式的模糊測試),并避免會導致非確定性執行的隱藏輸入等。在針對代碼進行模糊測試時,我們希望擁有盡可能多的確定性。
避免或控制模糊測試的阻礙
模糊測試的阻礙就是那些無法模糊化的東西。典型的模糊測試阻礙是包含在輸入中某處的校驗和:基于突變的模糊器隨機修改輸入會導致校驗和不合法,從而降低代碼覆蓋率。這個問題基本上有兩種解決方案。第一種,在模糊測試構建中關閉校驗和驗證;第二種,確保模糊器能夠生成帶有合法校驗和的輸出。基于生成的模糊器自帶這種功能;如果使用基于突變的模糊器,那么我們需要編寫一個小工具,在生成測試用例后,我們需要趕在測試用例傳遞給模糊測試程序前,給測試用例加上有效的校驗和。alf支持這種方法。
除了校驗和之外,難以滿足的輸入驗證屬性也是一個嚴重的問題。例如,如果你要針對強類型編程語言的編譯器進行模糊測試,那么盲目地修改編譯器的輸入很難獲得有效的編譯器輸入。我喜歡將有效性的約束分為軟約束(無效輸入除了浪費時間外,并沒有其他害處)和硬約束(系統在處理無效輸入時可能會暴走,因此必須避免無效輸入,否則完全無法進行模糊測試)。如果我們通過針對C++編譯器開展模糊測試來尋找錯誤代碼中的bug,就會面臨硬有效性約束,因為會導致未定義行為的編譯器輸入看上去很像是代碼中有bug。對于這類問題,我們沒有簡單的通用解決方案,只能通過一系列技巧來考慮有效性屬性。有一個最簡單的解決方案(但往往不是正確的解決方案),那就是自己編寫一個模糊器。但問題在于,如果自己編寫模糊器,就無法再利用現代覆蓋驅動的模糊測試技術——這種技術非常了不起的。為了匹配覆蓋率驅動的模糊測試框架,你有以下幾種選擇:首先,編寫一個能夠滿足有效性約束的自定義變異器;其次,采用了解結構的模糊,意思是說從模糊器中獲取修改后的數據,并將其轉換為模糊測試程序所需要的內容。關于如何讓覆蓋驅動的模糊器在有效性約束下依然良好地運行,且不需要大量的手動工作,我們還有大量的研究工作需要展開。這其中涉及很多細節,改日再深入介紹。一般來說,在模糊器中加入類似SAT的求解器并不能解決這個問題,其原因首先是,有的有效性約束(比如校驗和)對于求解器來說難度特別大;其次是因為有些有效性約束(如C++程序中的未定義行為)是隱含的,我們無法從系統中推斷,甚至從原理上也不可能。
一般來說,你無法通過為公共API提供輸入的方式,來針對系統的大部分代碼進行模糊測試,因為這些訪問會被系統中的其他代碼阻止。例如,如果你使用自定義的內存分配器實現或自定義的哈希表實現,那么應用程序級別的模糊測試可能無法針對分配器或哈希表進行有效的模糊測試。這些API應該直接暴露給模糊測試。單元測試和模糊測試有強烈的聯系:如果其中一個可行且可取,那么另一個也應該差不多。通常你應該同時兼顧兩者。
通常,Sanitizer和模糊器需要對構建過程進行調整,甚至是重大的改動。為了簡化這一過程,請盡可能簡化構建過程。確保可以輕松切換編譯器以及修改編譯器選項。盡量減少對特定工具(以及工具版本)的依賴。定期利用多個編譯器構建和測試代碼。構建系統的特殊依賴都需要記錄下來。
最后,有些模糊測試的阻礙有點傻而且很容易避免。如果你的代碼存在泄漏內存的問題,或者過深的調用棧會導致進程終止,那么使用持久模式的模糊測試是一件痛苦的事情,所以請盡量避免這些問題。盡量不要處理SIGSEGV信號,如果無法避免的話,那么應當有辦法在模糊器構建中禁用相應的信號處理程序。如果你的代碼無法與ASan或UBSan兼容,那么這些非常有用的預言就很難善加利用了。特別是,如果你的代碼使用自定義的內存分配器,那么你應該考慮在模糊測試構建中將其關閉,或者經過調整后與ASan一起使用,否則就有可能漏掉重大bug。
為覆蓋驅動的模糊器排除障礙
由于覆蓋驅動的模糊器的主要精力放在了測試未覆蓋的分支上,因此可能會被特殊的方式阻礙。例如,如果覆蓋驅動的模糊器遇到了太多未覆蓋的分支,它就會在這些分支上花費大量時間,導致覆蓋程序其他分支的可能性降低。例如,有一次我比較了一個程序在使用和不使用UBSan兩種情況下的afl覆蓋率,結果發現(在我設定的時間限制下)sanitized程序的覆蓋率要比沒有sanitized的程序低得多。但另一方面,我們也希望模糊器能找到sanitizer的錯誤。我的建議是有sanitized和沒有sanitized的程序都進行模糊測試。我不知道應該如何為這些模糊測試活動分配資源,也不知道是否有人在研究這個問題。也許這個問題并不重要,因為模糊測試本來就是過度測試。
有時候,你的程序在執行前期調用的代碼會包含大量分支且會被過度模糊的代碼。例如,可能你需要在處理輸入之前對輸入進行解壓縮或解密。這很可能會影響基于覆蓋的模糊器,導致模糊器花費大量時間去模糊測試加密庫或壓縮庫。如果你不是希望這樣,那么就應該提供一種方法,在模糊測試過程中禁用加密或壓縮。
程序中的解釋器都可能會給基于覆蓋的模糊器造成困難,因為相關的程序執行路徑是在解釋器數據中編碼的,而對于模糊器而言,一般情況下數據是不透明的。如果你想讓基于覆蓋的模糊器發揮最大作用,那么就應該考慮避免編寫解釋器,或者至少大力簡化解釋器。解決嵌入式解釋器有一個很明好的方法(我相信肯定有人嘗試過,不過我不知道)就是提供一個API,告訴模糊器該如何觀察被解釋語言的覆蓋率。
支持高吞吐量的模糊測試
模糊測試對于高吞吐量的系統最有效,特別是對于基于反饋的模糊器而言,這種模糊器需要一定時間來學習怎樣才能測試難以覆蓋的目標。有關吞吐量的一個簡單技巧是:提供禁用慢速代碼(例如詳細日志)的方法。類似地,干預I/O可以讓我們不需要借助運行速度技巧,比如在內存盤上運行模糊器等方法。
“但我希望模糊我的代碼變得更難,而不是更容易”
我不太贊同這個觀點。我們不應該期待模糊代碼來保證安全,而應該采用以下方法:
盡早、盡可能徹底地實施模糊測試,在將代碼發布到外部之前消除模糊測試能夠發現的缺陷。
通過人見人愛的強類型系統的編程語言編寫代碼——這可以靜態地消除由于錯誤的編程習慣造成的問題,例如可以防止我們將錯誤的東西放入哈希映射。
積極地使用斷言和sanitizer來動態檢查類型系統無法靜態強制執行的屬性。
反模糊技術的確存在,但我不認為它代表軟件朝著更好的方向發展。
總結
隨機測試非常強大,我們理應善加利用:如果你不針對你的代碼展開模糊測試,那么別人也會。本文向軟件開發人員介紹了一些實施模糊測試的好方法。當然還有很多其他方面本文未能提及,比如選擇一個優秀的語料庫以及編寫一個良好的模糊驅動程序。
原文:https://blog.regehr.org/archives/1687
本文為 CSDN 翻譯,轉載請注明來源出處。
【END】
?熱 文?推 薦?
?頂配 12699 元、沒有 5G,“浴霸三攝”的 iPhone 到底長什么樣??Mate 30 不預裝任何谷歌應用;阿里巴巴發布新“六脈神劍”;VS Code 1.38 發布 | 極客頭條?超 60 萬 GPS 定位服務被曝漏洞,用戶信息或將暴露!?2億日活,日均千萬級視頻上傳,快手推薦系統如何應對技術挑戰??Docker容器化部署Python應用?給面試官講明白:一致性Hash的原理和實踐?預警,CSW的50萬枚塵封BTC即將重返市場??她說:行!沒事別嫁程序員!你點的每個“在看”,我都認真當成了喜歡
總結
以上是生活随笔為你收集整理的隐藏画质代码_优秀的模糊测试代码是如何炼成的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: viewBox视图缩放(1)
- 下一篇: linux java socket编程_