大語言模型擁有的強大能力可以用來輔助多種工作,但如何有效的輔助仍然需要人的精巧設計。分享一篇發表于2024年CCS會議的論文PromptFuzz,它利用模型提示生成模糊測試驅動代碼,并將代碼片段嵌入到LLVM框架中執行模糊測試。
論文摘要
制作高質量的模糊測試驅動程序不僅耗時而且還需要對被測目標有深入的了解,即使是最先進的自動化模糊測試驅動程序生成技術也未能達到預期。雖然用被測目標代碼派生(OSS-Fuzz)的方式可以達到深度狀態,但是程序邏輯的覆蓋范圍有限。解釋性模糊測試(Hopper)可以探索多數接口調用,不過需要在較大的搜索空間進行多次嘗試。
論文提出了 PromptFuzz ,一種覆蓋引導的模糊器,它可以迭代生成模糊測試驅動程序來探索未被發現的庫程序代碼。通過使用大模型提示詞探索被測程序的接口調用,本文提出了幾種關鍵技術,包括:1)指導程序生成,2)錯誤程序驗證,3)覆蓋引導的提示變異,4)變量約束的模糊器調度。PromptFuzz 在 14 個真實的庫程序上進行了評估,模糊測試驅動程序的分治覆蓋率相比于 OSS-Fuzz 和 Hopper 分別高出 1.61 倍和 1.63 倍。此外,所提方案在 49 次崩潰中檢測到了 33 個新的漏洞,其中 30 個漏洞已得到相應社區的確認。
1 背景介紹
模糊測試對軟件的安全性和可靠性至關重要。OSS-Fuzz為開源軟件部署了最先進的模糊測試器,截至2023年2月,已在850個項目中發現并解決了8900多個漏洞和28000個錯誤。開發者會選擇合適的模糊測試器(Fuzzer)并編寫高質量的模糊測試驅動程序(Fuzz Driver),驅動程序會解析來自模糊測試器的輸入并調用被測目標(Target or Library)的程序代碼。然而,編寫高質量的模糊測試驅動程序具有挑戰性,因為它既耗時又需要對被測目標有深入的了解。手動編寫的模糊測試驅動程序通常只調用了被測目標的一小部分功能,限制了模糊測試的能力。
與手動編寫的模糊測試驅動程序相比,自動化技術通過從源代碼或運行時反饋中學習被測目標的接口調用情況,從而派生出模糊測試驅動程序。FUDGE,FuzzGen,UTopia方案從源代碼中采用靜態分析的方式提取接口調用代碼,而APICraft,WINNIE則從進程執行中動態跟蹤記錄接口的調用順序。Hopper是最先進的模糊測試驅動程序生成解決方案(與本文同一團隊的工作,發表于2023年CCS會議),它會將對被測目標的模糊測試問題轉化為解釋性模糊測試問題,從接口調用的動態反饋中學習有效的接口使用情況。盡管可以覆蓋到大多數接口函數,但Hopper需要在廣闊的搜索空間中進行多次嘗試,才能找到有用且滿足深度的接口調用序列。
大語言模型(LLM)在生成代碼方面有出色的表現,可以在不依賴被測目標代碼的情況下可以有效地探索接口使用情況。以GPT系列為例,它們在廣泛的代碼預料庫上進行過訓練,能夠生成符合用戶意圖的代碼。之前的工作也嘗試使用LLM生成模糊測試驅動程序,但它們設計的指令僅限于特定場景,生成的驅動程序在接口調用上多樣性較低,無法覆蓋不常用代碼或深度狀態。本文引入了PromptFuzz,一種覆蓋引導的模糊測試器,它會迭代地改變提示詞以探索未發現的庫程序代碼。
2 基礎概念
- 庫程序模糊測試
庫程序在軟件開發中被廣泛使用,因此針對它的模糊測試變得越來越重要。與命令行程序不同,庫程序擁有多個訪問入口點,即程序接口函數,這些入口有嚴格的格式約束規范。為了能夠利用現有的模糊測試器,相應的模糊測試驅動程序被開發出來,驅動程序從模糊測試器接受隨機字節,然后將這些字節轉換成結構良好的接口調用參數,喂給被測目標執行模糊測試。
論文給出了一個模糊測試驅動程序的例子,該驅動程序嵌入在LLVM框架中,每次執行一個測試用例。驅動程序接受數據和大小兩個參數,被測目標為視頻解碼庫libvpx,驅動程序執行初始化和數據轉換操作,調用庫程序的接口函數進行視頻解碼。
#include <vpx/vp8dx.h>
#include <vpx/vp8cx.h>
#include <vpx/vpx_decoder.h>extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {// Create the decoder configurationvpx_codec_dec_cfg_t dec_cfg = {0};...// Initialize the decodervpx_codec_ctx_t decoder;vpx_codec_iface_t *decoder_iface = vpx_codec_vp8_dx();vpx_codec_err_t decoder_init_res = vpx_codec_dec_init_ver(&decoder, decoder_iface, &dec_cfg, 0, VPX_DECODER_ABI_VERSION);if (decoder_init_res != VPX_CODEC_OK) {return 0;}// Process the input datavpx_codec_err_t decode_res = vpx_codec_decode(&decoder, data, size, NULL, 0);if (decode_res != VPX_CODEC_OK) {vpx_codec_destroy(&decoder);return 0;}// Get the decoded framevpx_image_t *img = NULL;vpx_codec_iter_t iter = NULL;while ((img = vpx_codec_get_frame(&decoder, &iter))!= NULL) {// Process the framevpx_img_flip(img);...}// Cleanupvpx_codec_destroy(&decoder);return 0;
}
- 大語言模型
LLM是一種深度學習模型,具有非常復雜的架構和大量的參數,使得它們能夠從大量文本數據中獲取知識,GPT3,ChatGPT,GPT4是當前十分具有代表性的LLM。大語言模型被訓練為預測下一個詞,表示為 w n + 1 w_{n+1} wn+1?,給定一個詞序列 w 1 , w 2 , . . . , w n w_1,w_2,...,w_n w1?,w2?,...,wn?,最大化語言模型的目標函數,如下列公式。
P ( w 1 , w 2 , . . . , w n ) = ∏ i = 1 n P ( w i ∣ w 1 , w 2 , . . . , w i ? 1 ) P(w_1,w_2,...,w_n)=\prod^n_{i=1}P(w_i|w_1,w_2,...,w_{i-1}) P(w1?,w2?,...,wn?)=i=1∏n?P(wi?∣w1?,w2?,...,wi?1?)
在推理階段,LLM利用廣泛參數中學習到的模型權重,基于先前的token
w 1 , w 2 , . . . , w n w_1,w_2,...,w_n w1?,w2?,...,wn?,自動回歸地生成下一個token
w n + 1 w_{n+1} wn+1?,用戶提供的起始token
被稱為提示詞。為了確保LLM產生的輸出與用戶給定指令保持一致,一系列LLM已通過強化學習訓練得到增強,例如ChatGPT和GPT4。
- 基于大語言模型的模糊測試驅動程序生成
最近,出現一些利用LLM來增強模糊測試的研究,其主要的挑戰包括自動構造提示詞和對模型輸出的驗證。在基于LLM的模糊測試驅動程序生成中,提示詞通常由任務描述和上下文信息組成。為了盡可能提供信息和指導模型,任務描述應至少制定被測目標庫程序,以及包含在其中的接口函數。在早期的嘗試中,每個提示詞僅分配一個接口函數作為目標,過于簡單而且難以有效果。另一方面,LLM生成的代碼無法直接用于模糊測試,因為它產生的代碼很容易出錯。依賴編譯器或簡單規則進行輸出的驗證,只能報告語法或淺層邏輯錯誤,當其用作模糊測試驅動程序時,這種缺陷代碼會產生許多誤報。
3 系統設計
PromptFuzz通過覆蓋率引導的LLM提示詞生成高質量的模糊測試驅動程序以檢測庫程序錯誤,它會改變LLM提示詞以生成涵蓋更廣泛接口調用范圍的驅動程序,首先隨機選擇一個庫接口函數構造提示詞,然后根據覆蓋率反饋改變該提示詞,直到模糊測試達到被測目標的收斂,工作流程如圖1所示。
3.1 指導程序生成
論文選擇ChatGPT和GPT-4作為大語言模型來指導模糊測試驅動程序生成,盡管模型生成的程序并不總能夠嚴格遵循指令,但它們有助于探索有效的庫程序接口調用情況,可以用提示詞來引導大語言模型生成符合預期的程序。PromptFuzz使用目標庫程序和接口函數組合填充提示詞模板,如圖2所示包含以下組件。
- 任務描述。說明了LLM應生成的驅動程序代碼,指定了庫程序的哪些接口函數在
LLVMFuzzerTestOneInput
函數中是必需的。 - 庫程序上下文。包括了庫程序使用的頭文件,接口函數簽名,自定義類型等信息,通過整合對庫程序上下文的理解,顯著減少LLM產生幻覺的發生。
- 庫程序說明。指導LLM生成符合庫程序所需指定模式的代碼,部分庫程序的接口函數可能從文件,文件流或描述符讀取輸入,對其進行相應規范。
3.2 錯誤程序驗證
PromptFuzz消除錯誤驅動程序通過如下三個步驟。
- 刪除C/C++編譯器識別出語法錯誤的驅動程序。
- 剩余的驅動程序編譯成可執行文件,結合多個運行時
sanitizers
,捕獲和分析與預期行為模式的偏差。 - 使用提供的語料庫對這些驅動程序進行模糊測試,刪除檢測到偏差的任何驅動程序。
在這部分模糊測試過程中,觸發獨特行為的輸入將添加到語料庫中,從而擴展進行更深入的運行時驗證。PromptFuzz同時還會計算驅動程序執行的代碼覆蓋率,刪除那些不符合代碼覆蓋標準的驅動程序,表明庫程序的接口函數得到了充分的利用,錯誤驅動程序驗證流程如圖3所示。
3.3 覆蓋引導的提示變異
為了創建連續多輪次的提示詞,PromptFuzz會改變前幾輪提示詞中的接口函數組合,生成不同的模糊測試驅動程序,同時以代碼覆蓋率作為反饋來生成有效的提示詞。
- 能量分配
首先,PromptFuzz為每個接口函數分配相同的能量,在每次模糊測試迭代期間,更新訪問過的分支并計算接口函數的分支覆蓋率。
c o v ( i ) = 包含 i 的覆蓋的分支數 包含 i 的所有的分支數 cov(i)=\frac{包含i的覆蓋的分支數}{包含i的所有的分支數} cov(i)=包含i的所有的分支數包含i的覆蓋的分支數?
接著,按照AFLFast中的指數調度來更新其能量,令 s e e d ( i ) seed(i) seed(i)為調用接口函數 i i i的種子驅動程序數量, p r o m p t ( i ) prompt(i) prompt(i)表示為包含接口函數 i i i的提示詞數量,計算能量如下。
e n e r g y ( i ) = 1 ? c o v ( i ) ( 1 + s e e d ( i ) ) e × ( 1 + p r o m p t ( i ) ) e energy(i)=\frac{1-cov(i)}{(1+seed(i))^e \times (1+prompt(i))^e} energy(i)=(1+seed(i))e×(1+prompt(i))e1?cov(i)?
執行越少次數的接口函數將被分配更高的能量,于是在未來的提示詞中包含該接口函數的概率就越高。
- 變異策略
然后,PromptFuzz會改變提示詞中的接口函數組合以指導模糊測試驅動程序生成,這些策略與傳統的模糊測試器類似,例如在C組合中插入A函數Insert(C,A)
,替換C組合中的A函數為B函數Replace(C,A,B)
,合并C組合和S組合形成新的組合CrossOver(C,S)
。
在接口函數能量的指導下,PromptFuzz通過變異策略組合接口函數以生成之前未探索過的組合,用density
表示調用顯式數據依賴的庫程序接口函數的最大數量,用unique branches
表示驅動程序執行時觸發的獨立分支數量。計算quality(g)=density(g) x (1+unique_branches(g))
用以量化驅動程序的質量,以質量值更高為目標來指導變異策略
在每次模糊測試迭代過程中,PromptFuzz都會探索驅動程序種子集合并更新這些種子驅動程序的質量,使用庫程序接口函數能量和種子驅動程序質量的反饋,應用如圖4所示算法來選擇下一次迭代中使用的新接口函數組合。
3.4 變量約束的模糊器調度
為了使得種子驅動程序能夠執行模糊測試,PromptFuzz對其中的接口函數參數進行約束類型推斷,將部分參數從常量轉換為從模糊測試器輸入的任意字節的變量,例如數組長度、文件名、格式化字符串等,一個簡單的實例如圖5所示。
最終,PromptFuzz將種子驅動程序整合到模糊測試器,根據其提供的幾個特定字節來調度每個種子驅動程序,3.2節中進行錯誤程序驗證的模糊測試語料將用來作為驅動程序的初始輸入。此外,轉換之前的參數常量值也將形成初始語料,配合以進行模糊測試器的執行。
4 實現驗證
PromptFuzz用Rust語言實現了大約1萬7千行代碼,開源在倉庫https://github.com/PromptFuzz/PromptFuzz中,其中使用了clang_ast
做抽象語法樹的提取,額外實現了FSan
插件來進行文件描述檢查。論文在14個廣泛使用的開源項目上對PromptFuzz進行了評估,總體實驗結果,發現的已知漏洞,消融實驗和變異策略的比較,可以詳細去看原論文,這里不再贅述。
學習筆記
這篇論文做了很好的嘗試,利用大語言模型來生成針對被測目標的驅動程序,再使用傳統的模糊測試器來執行測試,相關工作OSS-Fuzz也做過嘗試。論文偏工程,有一定啟發,方法比較雜,與近期部分LLM+Fuzz方向的研究類似,大語言模型用于輔助仍需要進行提純,通過算法篩選和糾偏,但確實在自動化和發散性方面比較有效。最后,附上文獻引用和DOI鏈接:
Lyu Y, Xie Y, Chen P, et al. Prompt Fuzzing for Fuzz Driver Generation[C]//Proceedings of the 2024 on ACM SIGSAC Conference on Computer and Communications Security. 2024: 3793-3807.
https://doi.org/10.1145/3658644.3670396