基于深度學習的源代碼缺陷檢測研究綜述
點擊下方卡片,關(guān)注“新機器視覺”公眾號
重磅干貨,第一時間送達
源自:軟件學報 作者:鄧梟 葉蔚 謝睿 張世琨
摘
要
源代碼缺陷檢測是判別程序代碼中是否存在非預期行為的過程, 廣泛應(yīng)用于軟件測試、軟件維護等軟件工程任務(wù), 對軟件的功能保障與應(yīng)用安全方面具有至關(guān)重要的作用. 傳統(tǒng)的缺陷檢測研究以程序分析為基礎(chǔ), 通常需要很強的領(lǐng)域知識與復雜的計算規(guī)則, 面臨狀態(tài)爆炸問題, 導致檢測性能有限, 在誤報漏報率上都有較大提高空間. 近年來, 開源社區(qū)的蓬勃發(fā)展積累了以開源代碼為核心的海量數(shù)據(jù), 在此背景下, 利用深度學習的特征學習能力能夠自動學習語義豐富的代碼表示, 從而為缺陷檢測提供一種新的途徑. 搜集了該領(lǐng)域最新的高水平論文, 從缺陷代碼數(shù)據(jù)集與深度學習缺陷檢測模型兩方面系統(tǒng)地對當前方法進行了歸納與闡述. 最后對該領(lǐng)域研究所面臨的主要挑戰(zhàn)進行總結(jié), 并展望了未來可能的研究重點.
關(guān)鍵詞
深度學習 缺陷檢測 代碼表征
1 引言
隨著越來越多的行業(yè)使用軟件作為其業(yè)務(wù)載體, 代碼已經(jīng)成為支撐社會正常運轉(zhuǎn)的最基本元素之一, 軟件的安全性問題也正在成為當今社會的根本性和基礎(chǔ)性的問題. 軟件質(zhì)量已經(jīng)直接影響了人們的日常生活.美國國家標準與技術(shù)研究院的一項技術(shù)報告指出, 軟件缺陷每年對美國造成的經(jīng)濟損失高達600億美元[1].
然而, 由于傳統(tǒng)的缺陷檢測需要專業(yè)人員花費較多的人力, 其在軟件開發(fā)過程中一直占用了較高的成本.實證研究表明, 缺陷檢測與修復耗費成本占軟件開發(fā)所有成本的50%?70%[2]. 因此, 人們對自動化缺陷檢測的需求日益增長.
隨著開源軟件的蓬勃發(fā)展, 研究者們能夠獲取的代碼量與缺陷信息越來越多. 成立于2008年的開源項目托管網(wǎng)站GitHub, 截至2020年, 已有超過5 000萬名用戶、1億個開源項目和2億的提交請求, 覆蓋了超過4.28億個文件. 截至2021年9月, 缺陷名錄網(wǎng)站NVD已經(jīng)收錄了171 178條缺陷數(shù)據(jù). 這些代碼數(shù)據(jù)為基于學習的缺陷檢測研究提供了充分的數(shù)據(jù)基礎(chǔ).
近年來, 大規(guī)模的數(shù)據(jù)和硬件方面不斷提升的計算能力, 使得深度學習技術(shù)在圖像處理、語音識別、自然語言處理等領(lǐng)域的多項任務(wù)上取得了突破性的進展, 促成了人工智能的第三次浪潮. 于是, 研究者們也逐漸開始在代碼領(lǐng)域應(yīng)用深度學習來嘗試替代傳統(tǒng)的研究方法, 期望通過深度學習來解決現(xiàn)有方法無法解決的一些問題.
經(jīng)過近5年的探究, 深度學習對缺陷代碼特征挖掘的能力得到了一定的驗證, 也吸引了越來越多的研究者從傳統(tǒng)缺陷檢測方法轉(zhuǎn)向使用深度學習. 同時, 深度學習方法應(yīng)用于源代碼缺陷檢測在數(shù)據(jù)集構(gòu)建和模型設(shè)計方面依然面臨眾多挑戰(zhàn). 本綜述即聚焦于基于深度學習的源代碼缺陷檢測技術(shù)研究.
1.1
術(shù)語定義
軟件源代碼安全作為軟件安全的重要研究點, 其核心內(nèi)容是對軟件源代碼進行缺陷檢測. 源代碼缺陷即程序代碼中存在的某種破壞正常運行能力的問題、錯誤或隱藏的功能缺陷, 會導致軟件產(chǎn)品在某種程度上不能滿足用戶的需要[3]. 源代碼漏洞是源代碼缺陷的一種, 可導致攻擊者利用的缺陷稱為漏洞[4]. 當前很多研究特定于漏洞, 因此在總體概述中, 我們將兩者統(tǒng)稱為“缺陷”, 在特定上下文中會區(qū)分“缺陷”與“漏洞”.
1.2
傳統(tǒng)缺陷檢測技術(shù)概述
源代碼缺陷檢測是檢查并發(fā)現(xiàn)軟件系統(tǒng)中存在缺陷的主要手段之一, 也是成熟的工業(yè)軟件在開發(fā)過程中必不可少的一道工序. 其通過利用統(tǒng)計工具對軟件代碼進行各維度度量上的審計, 或利用分析工具分析軟件的執(zhí)行過程來查找并定位軟件的設(shè)計錯誤、編碼缺陷、運行故障. 從運行模式上看, 早期的缺陷檢測技術(shù)可依據(jù)是否需要運行待檢測程序劃分為靜態(tài)分析方法與動態(tài)分析方法: 靜態(tài)分析方法一般應(yīng)用于軟件的開發(fā)編碼階段, 其無需運行軟件, 而是通過掃描源代碼分析詞法、語法、控制流和數(shù)據(jù)流等信息來發(fā)現(xiàn)缺陷; 動態(tài)分析方法則一般應(yīng)用于軟件的測試運行階段, 在軟件程序運行過程中, 通過分析動態(tài)調(diào)試器中程序的狀態(tài)、執(zhí)行路徑等信息來發(fā)現(xiàn)缺陷. 在該領(lǐng)域幾十年的發(fā)展歷程中, 當前已有許多公開的缺陷檢測工具, 如: Coverity[5]、Klocwork[6]以及Cobot[7]等典型的通用缺陷檢測工具, 通過對程序源代碼進行靜態(tài)分析與規(guī)則判別來檢測系統(tǒng)缺陷; KLEE[8]、S2E[9]、Mayhem[10]等動態(tài)工具采用符號執(zhí)行這種動態(tài)分析方法進行缺陷檢測; libFuzzer[11]、Radamsa[12]以及AFL[13]等動態(tài)工具采用基于模糊測試的動態(tài)分析方法排查錯誤. 同時, 也有相關(guān)研究嘗試通過動靜態(tài)相結(jié)合的分析方法來提高檢測效率和準確率. 如: angr[14]集成了靜態(tài)分析和動態(tài)符號執(zhí)行, 能夠?qū)崿F(xiàn)自動化分析二進制文件; SAGE[15]結(jié)合使用了模糊測試和符號執(zhí)行, 并將動態(tài)符號執(zhí)行應(yīng)用在x86架構(gòu)的程序分析中. 近年來, 為了提高缺陷檢測的效率, 也出現(xiàn)了一些自動化或半自動化的缺陷挖掘工具, 比較有代表性的如: Bochspwn[16]對內(nèi)核層采用污點追蹤, 從而檢測用戶層泄露數(shù)據(jù)的行為; Digtool[17]針對Windows系統(tǒng), 可自動化捕獲程序執(zhí)行過程中觸發(fā)的缺陷; 谷歌團隊推出的Syzkaller[18]針對Linux內(nèi)核進行無監(jiān)督、覆蓋引導的模糊測試; RapidScan[19]缺陷掃描器則通過自動化執(zhí)行nmap、dnsrecon和wafw00f等多個安全掃描工具, 通過多個工具掃描結(jié)果的融合共同發(fā)現(xiàn)缺陷.
盡管擁有較長的研究歷史, 傳統(tǒng)的缺陷檢測方法依然存在由其工作模式帶來的難以避免的不足. 如傳統(tǒng)的靜態(tài)分析方法往往依賴于專家人工構(gòu)造缺陷模式, 隨著軟件與缺陷復雜性的增加, 人工構(gòu)造成本和難度過高, 且人的主觀性導致不同專家對缺陷的理解不一, 這些都會嚴重影響誤報率和漏報率. 動態(tài)分析方法中, 監(jiān)測目標程序的崩潰是模糊測試發(fā)現(xiàn)漏洞的重要依據(jù)之一, 因此測試效果嚴重依賴于輸入種子的質(zhì)量, 而測試用例的自動生成與變異存在較大的偶然性, 其存在測試冗余、測試攻擊面模糊、難以發(fā)現(xiàn)訪問控制漏洞和設(shè)計邏輯錯誤等問題[20]. 動態(tài)分析方法中的符號執(zhí)行方法雖然能以較少的測試用例覆蓋更多的程序路徑, 從而挖掘復雜軟件更深層次的缺陷, 但仍然存在路徑爆炸、約束求解難、內(nèi)存建模與并行處理復雜等問題[21], 單獨處理大型軟件系統(tǒng)時仍存在較大困難[22]. 因此, 雖然上述早期缺陷檢測方法已在各類小型規(guī)模軟件的缺陷檢測任務(wù)中取得了一定成果, 但在實際的代碼工業(yè)環(huán)境中應(yīng)對大型復雜軟件系統(tǒng)以及變化多樣的新型缺陷時, 通常無法滿足需求.
1.3
基于傳統(tǒng)機器學習的缺陷檢測
針對大型復雜軟件系統(tǒng)的缺陷檢測問題, 研究者一方面嘗試通過優(yōu)化和改造現(xiàn)有方法來突破傳統(tǒng)缺陷檢測方法在檢測能力上的瓶頸, 另一方面嘗試探索新的智能化軟件缺陷檢測方法. 直觀上認為, 大部分缺陷相關(guān)信息可以通過代碼分析得到, 即挖掘軟件缺陷的能力與分析代碼數(shù)據(jù)的能力緊密相關(guān), 而基于學習的方法非常適合于從海量數(shù)據(jù)中發(fā)現(xiàn)和學習規(guī)律. 理論研究中, Hindle等人[23]利用統(tǒng)計學的方法將編程語言和自然語言進行比較, 研究結(jié)果表明, 兩者具有非常相似的統(tǒng)計學特性, 甚至編程語言更加規(guī)整, 從而提出了代碼的“自然說”假設(shè). 受到這個假設(shè)的啟發(fā), 研究人員逐漸意識到運用統(tǒng)計學習方法對代碼中蘊含的規(guī)律進行分析和泛化的可行性.
早期基于學習的缺陷檢測研究主要使用機器學習方法, 如VCCFinder[24]針對數(shù)據(jù)形態(tài)設(shè)計人工特征并進行統(tǒng)計, 得到具有區(qū)分性的特征項, 在對樣本特征值進行One-Hot編碼后, 使用支持向量機對其進行分類. 但該類方法在效率與效果上均存在較大的不足: 一方面, 大多數(shù)機器學習方法依然需要專家人工構(gòu)造特征作為輸入, 其檢測能力受限于特征工程的質(zhì)量高低; 另一方面, 當前的機器學習方法在實際的檢測效果上存在較高的誤報率, 難以達到實際應(yīng)用的需求. 究其根本, 主要原因是機器學習模型對深層特征的挖掘能力有限.
1.4
基于深度學習的缺陷檢測
得益于深度神經(jīng)網(wǎng)絡(luò)在圖像識別、自然語言處理等任務(wù)上取得的巨大成功, 人們發(fā)現(xiàn), 深度學習方法在挖掘深層特征上更加具有優(yōu)勢. 一些研究者開始嘗試將其引入源代碼缺陷檢測任務(wù). 當前的結(jié)果顯示, 深度學習在缺陷檢測任務(wù)上同樣具有傳統(tǒng)方法和機器學習方法無法比擬的優(yōu)勢. 并且, 由于其研究歷史相對短暫, 還有相當深入的空間供研究者探究.
使用深度學習進行缺陷檢測任務(wù), 核心構(gòu)成即為兩部分: 缺陷代碼數(shù)據(jù)集與深度學習缺陷檢測模型. 缺陷代碼數(shù)據(jù)集是深度學習模型的學習基礎(chǔ)與特征來源, 其需要擁有足夠規(guī)模的代碼數(shù)據(jù)來對缺陷代碼與非缺陷代碼進行表征. 深度學習缺陷檢測模型則需要根據(jù)代碼的特性設(shè)置合適的網(wǎng)絡(luò)結(jié)構(gòu), 以充分挖掘缺陷代碼數(shù)據(jù)集中的特征, 從而得到區(qū)分缺陷代碼與非缺陷代碼的能力.
本文廣泛收集了當前將深度學習應(yīng)用于源代碼缺陷檢測的相關(guān)研究. 我們使用“vulnerability/defect/bug”等關(guān)鍵詞查詢了谷歌學術(shù)搜索、IEEE Xplore、ACM Digital Library、Springer、DBLP、arXiv以及中國知網(wǎng)CNKI等搜索引擎和數(shù)據(jù)庫, 共查詢到908條結(jié)果; 隨后, 通過“code/detection”等關(guān)鍵詞篩查文章摘要, 保留了其中237篇; 再由2人逐一對查詢結(jié)果的標題、摘要進行人工審查, 過濾不使用深度學習方法的無關(guān)內(nèi)容, 在出現(xiàn)分歧時共同進行二次討論; 最后根據(jù)文獻的引用情況進行查缺補漏. 最終, 我們一共搜集到149篇基于深度學習進行源代碼缺陷檢測的相關(guān)文章, 時間覆蓋2017年?2021年11月, 其詳細的年份分布情況如圖 1-1所示.

圖 1-1 不同年份論文發(fā)表分布
可以看到, 從2017年起, 深度學習開始被引入到源代碼缺陷檢測領(lǐng)域. 近年來, 該領(lǐng)域論文的發(fā)表數(shù)量呈逐年增加的趨勢. 該數(shù)據(jù)表明, 使用深度學習進行缺陷檢測的研究熱度在不斷增加.
本文在介紹深度學習缺陷檢測技術(shù)時優(yōu)先選擇了CCF分級較高的論文, 包括CCF-B及以上和部分高質(zhì)量的CCF-C級論文. 最終, 本文選取了67篇深度學習源代碼缺陷檢測相關(guān)論文進行詳細介紹, 其中相當一部分論文來自所涉及領(lǐng)域的高質(zhì)量會議和期刊, 例如USENIX Security會議(4篇)、CCS會議(2篇)、OOPSLA會議(2篇)、NeurIPS會議(2篇)、IJCAI會議(2篇)、ESEC/FSE會議(1篇)、ACL會議(1篇)、ICML會議(1篇)、TDSC期刊(4篇)、TOSEM期刊(2篇)、TSE期刊(1篇)、Proc. IEEE期刊(1篇)、《軟件學報》(1篇)等.
1.5
相關(guān)綜述
事實上, 在2019年底, 李韻等人[25]就已經(jīng)對基于機器學習的軟件漏洞挖掘方法進行了分類與分析, 其重點關(guān)注機器學習方法, 且主要關(guān)注2010?2019年的文獻. 而該領(lǐng)域中基于深度學習的方法作為近年的熱門方向, 其相關(guān)文獻主要發(fā)表于2019?2021年間, 因此本綜述與前人總結(jié)在技術(shù)關(guān)注點與文獻分布上均有較大的差異, 可以作為前文的補充與進一步的延申擴展. 此外, 還有一些針對缺陷檢測技術(shù)的英文綜述, 如Chakraborty等人[26]和Lin等人[27]的研究, 與本文的方向相近. 但此類綜述往往只關(guān)注深度模型, 而本文在覆蓋更新研究點的同時, 對數(shù)據(jù)集方面也進行了更為細致的分析.
本文從源代碼缺陷數(shù)據(jù)集構(gòu)造與深度學習缺陷檢測模型這兩個技術(shù)模塊對當前研究進行了分類歸納與整理, 并對目前該研究領(lǐng)域亟待解決的問題與未來可能的研究方向進行了闡述.
2 缺陷代碼數(shù)據(jù)集的構(gòu)造
由于使用深度學習進行缺陷檢測的研究近幾年才興起, 因此有別于一些經(jīng)典、成熟的深度學習任務(wù)擁有統(tǒng)一、公認的數(shù)據(jù)集, 當前在缺陷檢測領(lǐng)域并沒有一個用來統(tǒng)一評測的缺陷代碼數(shù)據(jù)集. 當前的研究往往是自行構(gòu)建一個缺陷代碼數(shù)據(jù)集, 作為衡量模型性能的數(shù)據(jù)基礎(chǔ). 這就導致各個研究使用不同的自建數(shù)據(jù)集. 然而, 在不同數(shù)據(jù)集上依靠數(shù)值指標來橫向比較不同模型的優(yōu)劣是不現(xiàn)實的, 因此, 當前該領(lǐng)域的研究往往會回避模型之間的比較問題. 不過目前為止, 還沒有研究針對性地指出數(shù)據(jù)集不統(tǒng)一帶來的問題. 同時, 自建缺陷代碼數(shù)據(jù)集的構(gòu)建過程也存在較多的模糊或近似的問題, 甚至存在一些不合理的操作, 這些都會嚴重地影響模型在其上的訓練表現(xiàn). 因此, 本文將數(shù)據(jù)集的構(gòu)造方法作為基于深度學習的缺陷檢測中一個重要的研究點, 著重關(guān)注這一部分的研究現(xiàn)狀與存在的問題.
不同于圖像處理、自然語言處理等任務(wù)擁有成熟、通用的數(shù)據(jù)集, 使用深度學習進行缺陷檢測面臨著巨大的數(shù)據(jù)集方面的問題. 究其根本, 主要有兩個核心難點.
(1) 在已知的真實項目中, 有缺陷的代碼數(shù)量有限. 缺陷本身就是重要資源, 有些甚至是涉及到更高層安全的戰(zhàn)略資源. 當前, 許多研究花費了大量精力進行標注, 但是不會公開或者僅部分公開; 代碼開源且已被公開確認收錄的缺陷數(shù)量少, 且對齊關(guān)系模糊. 例如NVD缺陷庫中的數(shù)據(jù), 至今已有17萬余條, 但經(jīng)過驗證, 能夠?qū)R到具體代碼的只有5 300余條, 細分至不同程序語言則數(shù)量更加有限, 難以支撐深度模型的訓練; 商業(yè)工具自行構(gòu)造并收集的測試數(shù)據(jù)集, 由于其巨大的商業(yè)價值不會輕易公開;
(2) 人工標注或自動化生成的難度和成本極高. 對于某些領(lǐng)域, 人工標注數(shù)據(jù)的成本是容易的, 例如圖片、文本、語音, 大部分標注任務(wù)普通人即可完成, 這就使得其標注的成本相對低廉, 可以通過眾包的方式完成. 但是對于專業(yè)的程序員, 識別缺陷也是極其費時耗力的工作.
因此, 缺陷代碼數(shù)據(jù)集作為深度學習缺陷檢測任務(wù)的基礎(chǔ), 具有很高的重要性, 但同時構(gòu)造難度較大.
2.1
缺陷代碼數(shù)據(jù)集的構(gòu)造方法分類
構(gòu)造缺陷代碼數(shù)據(jù)集, 從流程上可以分為3個主要技術(shù)環(huán)節(jié): 缺陷條目的獲取、缺陷代碼的抽取與處理、處理后樣本的標注. 因此, 對應(yīng)每一個技術(shù)環(huán)節(jié), 我們按照數(shù)據(jù)來源、樣本粒度與標簽來源對其進行分類, 如圖 2-1所示.

圖 2-1 缺陷代碼數(shù)據(jù)集的構(gòu)造方法分類
構(gòu)造缺陷代碼數(shù)據(jù)集, 首先需要獲取缺陷數(shù)據(jù), 即找到與缺陷相關(guān)的代碼. 從數(shù)據(jù)來源上劃分, 當前缺陷數(shù)據(jù)的來源主要分為4類: 人工構(gòu)造、缺陷庫抽取、代碼庫爬取與分析工具生成.
(1) 人工構(gòu)造即通過人工規(guī)則將正確代碼轉(zhuǎn)化為缺陷代碼;
(2) 缺陷庫抽取即以公開的缺陷數(shù)據(jù)庫為入口, 抽取指定的缺陷類型條目, 并提取對應(yīng)的缺陷代碼;
(3) 代碼庫爬取即直接檢索代碼倉庫, 從中爬取出具有缺陷特征的代碼提交;
(4) 分析工具生成即通過缺陷分析工具對源代碼進行缺陷檢測, 將檢測結(jié)果作為其是否含有缺陷的標簽.
當確認了代碼中包含缺陷后, 如何選擇合適的粒度成為關(guān)鍵問題. 粒度過大則可能帶來較多噪聲與冗余, 粒度過小則可能使得缺陷特征丟失. 同時, 不同的粒度適用的網(wǎng)絡(luò)結(jié)構(gòu)也不同, 良好的粒度與網(wǎng)絡(luò)組合能夠使得模型更好地捕獲缺陷特征. 當前主要的樣本粒度為文件級、函數(shù)級、切片級及其他類別.
(1) 文件級主要是將項目進行文件級拆分構(gòu)成數(shù)據(jù)集;
(2) 函數(shù)級大多數(shù)先獲得文件數(shù)據(jù), 再將其按照函數(shù)劃分為單個樣本;
(3) 切片級需要對代碼進行解析, 并根據(jù)設(shè)定規(guī)則進行切片.
當?shù)玫搅颂幚砗蟮臉颖竞? 需要對其進行是否含有缺陷的標注.
(1) 若缺陷樣本是通過人工規(guī)則生成的, 則在生成時即已擁有標簽, 可以自動完成標注;
(2) 對于從缺陷庫中抽取的缺陷數(shù)據(jù), 若采用與數(shù)據(jù)源相同的缺陷粒度, 則往往可以直接獲得其標簽, 可以自動完成標注;
(3) 若需要對缺陷進行更細粒度的處理, 則需要研究者自行進行細粒度的標注工作. 當前, 獲取標簽的方式主要分為自動標注與人工標注兩種.
為了便于介紹, 下面我們使用數(shù)據(jù)來源作為分類維度, 對當前的數(shù)據(jù)集構(gòu)造方法進行介紹.
2.1.1
基于人工構(gòu)造
在軟件開發(fā)過程中, 為了測試軟件的可靠性, 往往會人工撰寫一些測試樣例用于測試. 這些測試樣例因此成為了天然的缺陷樣本數(shù)據(jù).
Sard (software assurance reference dataset)[28]是美國國家標準與技術(shù)研究院發(fā)布的軟件防護參考數(shù)據(jù)集, 旨在為用戶、研究者與軟件安全工具開發(fā)人員提供已知的安全缺陷來對安全工具與方法進行測試評估. 該數(shù)據(jù)集收集了來自工業(yè)生產(chǎn)、人工構(gòu)造與學生撰寫的測試用例, 涵蓋C/C++、Java等7種語言, 當前, 該數(shù)據(jù)集共包含測試用例177 184條. Sard中的代碼按照Good (不含缺陷)、Bad (包含缺陷)與Mix (含有缺陷及補丁)進行分類, 并對缺陷部分明確標注了缺陷觸發(fā)位置. 雖然Sard數(shù)據(jù)集中的測試用例來源不盡相同, 但其均為人工撰寫用于測試的基礎(chǔ)樣例, 其缺陷流程較為簡單, 復雜度較低, 與真實的代碼缺陷存在一定差距.
OWASP Benchmark[29]項目是一個綜合的Java測試樣例集, 專為軟件漏洞分析工具的自動評估而設(shè)計. 當前, OWASP Benchmark測試套件有兩個主要版本(V1.1和V1.2), 包含11個類別的Web應(yīng)用程序漏洞的數(shù)千個標記測試用例. 其中, V1.1中擁有更多的測試用例, 但用例不可執(zhí)行也不可利用, 且正負樣本不平衡. V1.2對這些問題進行了改進, 其用例全部可執(zhí)行、可利用, 且正負樣本均衡.
在研究初期, 為了盡快獲得缺陷數(shù)據(jù), 很多缺陷代碼數(shù)據(jù)集是從缺陷庫中直接抽取出來的.
Xu等人[30]直接使用Sard數(shù)據(jù)集提供的數(shù)據(jù), 其選擇了其中緩沖區(qū)錯誤(buffer error, CWE-119)與資源管理錯誤(resource management error, CWE-399)這兩個類型, 共計23 185個程序, 其中, 11 093為缺陷程序, 占比47.8%.
Duan等人[31]也使用Sard數(shù)據(jù)集進行測試, 他們?yōu)榱颂骄咳绾胃玫夭蹲饺毕荽a與非缺陷代碼中的細小差距, 因此Sard數(shù)據(jù)集正好可以滿足該需求. 最終, 他們選擇了緩沖區(qū)錯誤(CWE-119)和資源管理(CWE- 399)這兩個類型, 按照函數(shù)級進行收集, 共計28 049個函數(shù), 其中, 缺陷函數(shù)為10 561個, 占比37.7%. 該數(shù)據(jù)集未開源.
Cao等人[32]使用了Sard數(shù)據(jù)集作為數(shù)據(jù)來源, 并同樣選取了其中CWE-399和CWE-119這兩類共計11 397個文件, 并按照函數(shù)進行分割與打標. 最終, 該數(shù)據(jù)集包含63 828個函數(shù), 其中, 缺陷函數(shù)為46 337個, 占比72.6%.
Saccente等人[33]在Sard的基礎(chǔ)上構(gòu)建了一個Java語言的函數(shù)級缺陷代碼數(shù)據(jù)集. 其首先選取了原數(shù)據(jù)集中類型下數(shù)量超過100的29個CWE缺陷類型, 共44 495個文件. 在預處理過程中, 其將對應(yīng)文件按函數(shù)級分割, 然后使用分詞器Javalang進行分詞, 以作為向量形式投入模型. 該數(shù)據(jù)集已開源(https://gitlab.com/TUSoftwareEngineering/vulnerabilitylocalization-using-machine-learning).
Feng等人[34]從Sard中提取了3個級別的針對C/C++語言的函數(shù)級缺陷代碼數(shù)據(jù)集. 其中, 針對Buffer Overflow的BO數(shù)據(jù)集包括CWE-121與CWE-122, 用于測試模型對最常見的單一類型缺陷的檢測能力; 針對多個類型的5K數(shù)據(jù)集包括樣本數(shù)量大于5 000的全部類型; 最后是整個Sard數(shù)據(jù)集用于完整的評估模型. 所有的數(shù)據(jù)均按照函數(shù)進行分割, 最終的完整數(shù)據(jù)集包含269 985個函數(shù), 其中, 缺陷函數(shù)為89 071個, 占比33%. 該數(shù)據(jù)集未開源.
為了滿足圖神經(jīng)網(wǎng)絡(luò)的輸入需求, Ghaffarian等人[35]構(gòu)建了一個由多種代碼圖組成的缺陷代碼數(shù)據(jù)集. 該數(shù)據(jù)集基于Java語言, 其從OWASP Benchmark v1.2中提取了7個類型的測試樣例共1 698條, 其中, 缺陷樣例為932條, 占比54.9%. 該數(shù)據(jù)集并未開源. 但作者開源了其跨平臺代碼解析工具PROGEX[36], 其用于標準化地對程序生成各種圖表示, 以便與后續(xù)的模型作快速的數(shù)據(jù)準備. 同時, 該工具還支持將各種生成圖以不同的文本格式存儲, 例如DOT、JSON、GML等.
雖然從測試樣例中抽取樣本較為快捷, 但其數(shù)量畢竟有限, 無法提供更大規(guī)模的缺陷樣本. 為此, 一些研究者嘗試自行構(gòu)造缺陷樣本, 其通過自行設(shè)定的規(guī)則, 對正確代碼變異來得到缺陷代碼.
為了利用代碼中變量與函數(shù)命名上的自然語言信息, Pradel等人[37]構(gòu)建了一個基于命名的缺陷數(shù)據(jù)集. 針對構(gòu)造數(shù)據(jù)集時存在的諸多困難, 他們選擇使用代碼轉(zhuǎn)換的方式, 將非缺陷代碼改寫為缺陷代碼, 從而極大地減少了構(gòu)造數(shù)據(jù)集所需的人力與計算資源. 具體來說, 其使用3種變異規(guī)則對代碼進行修改, 包括交換函數(shù)參數(shù)、修改二元操作符、修改二元運算中的操作數(shù). 這些操作都是在AST的基礎(chǔ)上進行的. 由于其默認大部分的代碼均為正確代碼(指的是不包含上述3種錯誤模式), 因此轉(zhuǎn)化前的代碼即為非缺陷代碼, 轉(zhuǎn)化后的即為缺陷代碼. DeepBugs的數(shù)據(jù)集由150 000個JavaScript文件生成, 這些文件均由開源工程收集并經(jīng)過了清洗與去重, 一共包含6 860行代碼. 在經(jīng)過上述變異操作后, 最終該數(shù)據(jù)集包含16 634 458個樣本. 該數(shù)據(jù)集已開源(https://github.com/michaelpradel/DeepBugs).
Allamanis等人[38]同樣采用了人工規(guī)則自動構(gòu)造缺陷數(shù)據(jù)的方法. 其在DeepBug[37]規(guī)則的基礎(chǔ)上, 增加了變量替換與常數(shù)替換兩種構(gòu)造規(guī)則. 其通過這一方法對PyPI中下載量前4 000名的Python項目進行了變換, 構(gòu)造了針對Python語言的缺陷代碼數(shù)據(jù)集. 該數(shù)據(jù)集已開源(https://www.microsoft.com/en-us/download/103554).
Choi等人[39]選擇直接生成缺陷代碼段, 其自行構(gòu)建了一個針對緩沖區(qū)溢出類型的函數(shù)級C語言缺陷代碼數(shù)據(jù)集. 代碼生成規(guī)則中采用與Sard相同的緩沖區(qū)訪問函數(shù)和初始化方法, 以保持至少相同級別的任務(wù)復雜性. 該數(shù)據(jù)集中每個構(gòu)造的樣本代碼段均為一個無返回值函數(shù), 其由3個階段構(gòu)成: 初始化變量、緩沖區(qū)分配和緩沖區(qū)訪問. 在緩沖區(qū)的使用上, 分為直接調(diào)用、使用API調(diào)用、利用變量分配和利用API調(diào)用與變量重劃分共4個不同難度級別. 該數(shù)據(jù)集最終包含10 000個樣本, 其中, 缺陷樣本占比50.1%. 不過, 該數(shù)據(jù)集中的樣本構(gòu)造規(guī)則與邏輯較為簡單: 其長度均限定在8?30行, 遠低于實際應(yīng)用場景中的待測函數(shù)代碼長度; 其對緩沖區(qū)的調(diào)用限定為4種模式, 均較為直接, 在程序復雜度上遠低于實際應(yīng)用場景. 該數(shù)據(jù)集已開源(https://github.com/mjc92/buffer_overrun_memory_networks).
總的來說, 基于人工構(gòu)造的方法通過特定的規(guī)則可以快速地生成大量的樣本. 然而, 由此構(gòu)建的缺陷代碼數(shù)據(jù)集只能表征構(gòu)造規(guī)則, 這會導致訓練出的深度模型只局限于對特定規(guī)則的識別, 而偏離其“缺陷檢測”的原始目標. 因此, 當前通用類型檢測的系統(tǒng)不再人工構(gòu)造缺陷數(shù)據(jù)樣本.
2.1.2
基于缺陷庫抽取
當前, 有一些機構(gòu)或組織對軟件開發(fā)中發(fā)現(xiàn)的缺陷進行了收集、整理與發(fā)布, 因此通過該類缺陷庫即可獲得缺陷條目入口. 但是該類缺陷庫大多只有部分條目能夠鏈接到缺陷對應(yīng)的源代碼, 因此基于缺陷庫抽取缺陷數(shù)據(jù)的方法大多需要先篩選出其中對齊源代碼成功的條目, 再進行后續(xù)的處理.
NVD (national vulnerability database)[40]是美國國土安全部下屬的聯(lián)邦計算機應(yīng)急準備小組維護的國家漏洞數(shù)據(jù)庫, 是當前較為權(quán)威的源代碼漏洞數(shù)據(jù)庫. 該庫實時同步CVE發(fā)布的條目, 在CVE基礎(chǔ)上提供安全評分、缺陷影響評級、修復信息、缺陷查找等功能, 并通過人工審核的方式將缺陷與原項目相關(guān)信息進行鏈接.由于其數(shù)據(jù)相對較為齊全, 人工對齊源碼出處的質(zhì)量相對較高, 因此該缺陷庫成為當前最常用的缺陷抽取源.
Lin等人[41]使用函數(shù)作為缺陷代碼的粒度, 其選擇了6個知名工程(LibTIFF、LibPNG、FFmpeg、Pidgin、VLC Media Player、Asterisk), 通過NVD檢索其相關(guān)缺陷, 并人工定位缺陷所在函數(shù), 剩余未被標記為缺陷函數(shù)的即作為非缺陷函數(shù)加入數(shù)據(jù)集. 最終, 該數(shù)據(jù)集包含32 988個函數(shù), 其中, 缺陷函數(shù)為457個, 占比1.4%, 不平衡的情況較嚴重. 該數(shù)據(jù)集已開源(https://github.com/DanielLin1986/TransferRepresentationLearning).
Li等人[42]提出了基于C/C++語言的切片級缺陷代碼數(shù)據(jù)集. 其檢索了NVD與Sard中緩沖區(qū)錯誤(CWE-119)與資源管理錯誤(CWE-399)這兩類缺陷條目, 選擇其中19個開源C/C++項目的相關(guān)缺陷. 其定義了代碼片段(code gadgets), 即語義上相互關(guān)聯(lián)的幾行代碼, 并用它來表示程序, 以使其避免函數(shù)級表示會帶來的無關(guān)噪聲信息. 其首先在源代碼中按照分析工具Checkmarx的危險函數(shù)標準查找?guī)旌瘮?shù)與API函數(shù)調(diào)用作為危險入口, 將其語句中的變量作為起點進行切片, 并按照自然代碼順序進行組合形成code gadget. 該切片只考慮數(shù)據(jù)依賴關(guān)系. 在標注是否含有缺陷時, 其依靠代碼片段中是否含有修復時被刪除或修改的語句來進行判斷: 若有, 則標注為缺陷片段. 最終, 該數(shù)據(jù)集包含61 638個代碼片段, 其中, 17 725個為缺陷片段, 占比28.8%. 不過, 其中僅有1.4% (840個)來自于真實項目缺陷(NVD). 該數(shù)據(jù)集已開源(https://github.com/CGCL-codes/VulDeePecker).
為了支持多分類缺陷檢測任務(wù), Zou等人[43]在VulDeePecker[42]數(shù)據(jù)集上進行了擴充, 增加了控制依賴作為切片依據(jù), 并將缺陷類型也加入其中, 而不僅僅記錄其是否含有缺陷. 其μVulDeePecker數(shù)據(jù)集在切片時同時進行前后向的切片, 并同時考慮數(shù)據(jù)依賴與控制依賴. 該數(shù)據(jù)集最終包含181 641個代碼片段, 其中, 43 119個為缺陷片段, 占比23.7%. 其共涉及40個CWE三級分類. 但是, 該數(shù)據(jù)集的缺陷樣本存在類型信息, 而非缺陷樣本均標記為非缺陷類型, 不存在類型信息, 可能會影響模型的判斷. 并且, 該數(shù)據(jù)集中來自NVD的真實缺陷占比不到1%. 該數(shù)據(jù)集已開源(https://github.com/muVulDeePecker/muVulDeePecker).
由于VulDeePecker[42]數(shù)據(jù)集只考慮了以危險庫/API函數(shù)調(diào)用作為入口的缺陷條目作為樣本, 因此只能提供兩種缺陷類型的樣本, 其覆蓋范圍較窄. Li等人[44]在其基礎(chǔ)上進行了改進, 使其同時兼顧語法特征與語義特征, 從而更全面地表征缺陷以覆蓋更多的缺陷類型. 該數(shù)據(jù)集SySeVR同樣從NVD與Sard數(shù)據(jù)庫中抽取缺陷, 其首先根據(jù)Checkmarx的危險變量規(guī)則提出了4種危險入口(庫/API函數(shù)調(diào)用、數(shù)組使用、指針使用、算數(shù)表達式), 從代碼中的危險入口開始切片. 與VulDeePecker數(shù)據(jù)集根據(jù)語句中的標識符進行切片不同的是, SySeVR數(shù)據(jù)集是在語句的程序依賴圖基礎(chǔ)上進行切片, 從而利用控制依賴與數(shù)據(jù)依賴信息完善缺陷特征. SySeVR的標簽方法與VulDeePecker數(shù)據(jù)集相同. 最終, SySeVR數(shù)據(jù)集包含420 627個代碼片段, 其中, 缺陷片段為56 395個, 占比13.4%. 該數(shù)據(jù)集已開源(https://github.com/SySeVR/SySeVR).
缺陷代碼與其修復版本同時在數(shù)據(jù)集中可以幫助模型更好地學習到其中的差異, 并且能夠保證數(shù)據(jù)集正負樣本的平衡. 為此, Xiao等人[45]構(gòu)建了一個針對C/C++的缺陷代碼與修復數(shù)據(jù)集. 其選定了10個不同領(lǐng)域的開源工程, 從NVD和項目代碼提交中檢索相關(guān)缺陷與修復, 根據(jù)修復的位置確定缺陷函數(shù). 該數(shù)據(jù)集只包含修復在單個函數(shù)內(nèi)部的. 最終, 該數(shù)據(jù)集包含25 377對缺陷與其修復. 不過, 該數(shù)據(jù)集未開源.
Nikitopoulos等人[46]構(gòu)建了一個多語言的缺陷代碼數(shù)據(jù)集CrossVul. 其人工審查了NVD提及的5 877個Github提交, 確認其鏈接的可用性, 并標記了缺陷及其對應(yīng)補丁所在文件, 并將提交信息也作為數(shù)據(jù)一同保存. 該數(shù)據(jù)集最終涉及1 675個項目, 包括27 476個文件(缺陷與非缺陷各半), 涵蓋40種編程語言, 涉及168個CWE類型, 每一條缺陷都記錄了其對應(yīng)的CVE ID與其來源鏈接. 不過, 該數(shù)據(jù)集只是文件級數(shù)據(jù), 并沒有進一步細化到函數(shù)甚至切片, 因此, 在缺陷檢測任務(wù)中使用可能需要較大工作量的細化標注工作. 該數(shù)據(jù)集已開源(https://doi.org/10.5281/zenodo.4734050).
Lin等人[47]提出了一個針對深度學習缺陷函數(shù)檢測的C語言評測集. 其根據(jù)NVD與CVE的描述信息, 通過人工的方式將其對齊到GitHub代碼庫, 并標注缺陷函數(shù)與缺陷文件. 其默認最新版本的工程中其他文件均為非缺陷文件, 因此將新版本中未被標記的文件和其中函數(shù)作為非缺陷樣本. 其標注了9個開源工程, 共計5 780個文件60 768個函數(shù), 其中, 缺陷函數(shù)為1 471個, 占比2.4%. 但是, 該數(shù)據(jù)集沒有考慮到缺陷類型等信息.
為了測試跨域?qū)W習方法的效果, Liu等人[48]提出了一個針對C/C++的缺陷代碼數(shù)據(jù)集. 其選用了VulDeePecker數(shù)據(jù)集中的CWE-119與CWE-399這兩類, 用來測試缺陷類型間的跨域. 其另外根據(jù)NVD與CVE信息人工標注了3個開源項目(LibTIFF、FFmpeg、LibPNG)的函數(shù)級缺陷數(shù)據(jù), 每個項目均抽取了9種缺陷類型, 用來測試項目間的跨域. 非缺陷樣本都是從項目中未被標記為缺陷樣本的函數(shù)中抽取的. 該數(shù)據(jù)集已開源(https://github.com/wolong3385/SVD-Source).
很多缺陷代碼數(shù)據(jù)集在構(gòu)造時都基于一個假設(shè): 工程代碼除了已知的缺陷以外沒有其他缺陷. 因此, 設(shè)計者將被標記為缺陷樣本以外的部分當作非缺陷樣本對待. 然而事實是, 新缺陷都是從這些“非缺陷樣本”中發(fā)現(xiàn)的. 因此, 這種假設(shè)會帶來許多噪聲從而影響模型的判別能力.
為此, Jimenez等人[49]放棄了缺陷樣本以外的代碼來避免這個問題, 他們只使用缺陷樣本和其對應(yīng)的修改后版本作為樣本來構(gòu)造數(shù)據(jù)集. 他們提出了一個自動的可擴展框架, 并由此構(gòu)建了缺陷代碼數(shù)據(jù)集VulData7. 其通過NVD作為缺陷入口, 選取4個C語言開源項目(Linux Kernel、Wireshark、OpenSSL、SystemD)的條目, 自動將其對齊到項目代碼庫及相應(yīng)提交. VulData7記錄了每條缺陷的報告信息(描述、CVE編號、CWE編號、CVSS嚴重程度)、影響版本列表、修復提交和修復前后文件. 當前版本的VulData7包含2 809條缺陷, 其中, 1 598條附帶了修復信息. 其更新機制會不斷同步NVD以不斷維護數(shù)據(jù)集. 該數(shù)據(jù)集已開源(https://github.com/electricalwind/data7).
Clemente等人[50]從缺陷跟蹤網(wǎng)站Bugzilla、Mozilla Foundation Security Advisory (MFSA)和Computer Vulnerability Exposure site (CVE)上提取信息構(gòu)建缺陷數(shù)據(jù)集. 其將搜索范圍限定在Mozilla的C++項目Firefox上, 根據(jù)網(wǎng)站的報出提取出缺陷相關(guān)文件. 最終, 該數(shù)據(jù)集包含395個文件, 其中有200個缺陷樣本, 占比50.6%. 該數(shù)據(jù)集未開源.
除了著名的通用缺陷庫以外, 許多大型軟件開發(fā)商都會安排安全團隊自行維護其軟件產(chǎn)品的安全記錄, 這些安全記錄也可以為構(gòu)建缺陷代碼數(shù)據(jù)集提供可靠的信息.
Alexopoulos等人[51]提出了一種自動構(gòu)建缺陷數(shù)據(jù)集的方法. 他們從Debian安全團隊維護的缺陷庫Debian Security Advisories (DSAs)中進行信息挖掘. 首先, 從DSA的缺陷報告中可以直接提取出報出的缺陷與對應(yīng)的NVD鏈接, 同時還包括其CWE缺陷類型與嚴重等級; 隨后, 使用官方提供的快照功能下載每個月的工程源代碼; 再通過開源工具PKGDIFF對相鄰版本的修改信息進行生成, 從而定位出缺陷報告是在何時被引入. 該數(shù)據(jù)集最終選用了7個Debian項目, 分別是Linux kernel、Firefox、Chromium、PHP、OpenJDK、Thunderbird和Wireshark. 不過, 由于該研究還未完全完成, 該數(shù)據(jù)集未開源.
Fan等人[52]提出了一個針對C/C++語言的函數(shù)級缺陷代碼數(shù)據(jù)集Big-Vul. 其通過爬取CVE數(shù)據(jù)庫和相關(guān)源代碼倉庫中的GitHub鏈接, 定位了348個GitHub項目, 并對齊到每個缺陷的修復提交. 其將函數(shù)在修復前后兩個版本與CVE描述信息一并保存, 記錄了包括CVE ID、嚴重程度、描述等21個特征值. 項目中的其他非缺陷相關(guān)函數(shù)作為正確函數(shù)也加以保留. 最終, Big-Vul數(shù)據(jù)集包含3 754個代碼缺陷, 覆蓋91個缺陷類型, 共含有11 823個缺陷函數(shù)與253 096個非缺陷函數(shù), 缺陷函數(shù)占比4.5%. 該數(shù)據(jù)集已開源(https://github.com/ZeoVan/MSR_20_Code_Vulnerability_CSV_Dataset).
Li等人[53]從tera-PROMISE缺陷數(shù)據(jù)庫中抽取針對Java語言的文件級缺陷代碼數(shù)據(jù)集. 其選擇了7個開源Java項目包含的缺陷條目, 每個項目都選擇了連續(xù)的兩個版本. 從其對應(yīng)的GitHub倉庫中爬取了源文件, 并按照報出缺陷位置對文件進行標注. 最終, 該數(shù)據(jù)集包含3 290個文件, 其中, 缺陷文件為1 152個, 占比35%. 該數(shù)據(jù)集未開源.
Zhang等人[54]同樣從tera-PROMISE數(shù)據(jù)集中抽取缺陷數(shù)據(jù), 其選擇了12個Java語言的開源項目, 共計5 194個文件, 其中, 缺陷文件為1 617個, 占比31.1%. 該數(shù)據(jù)集同時還包含了20種傳統(tǒng)代碼度量信息.
Ponta等人[55]將NVD和50余個項目自身的安全記錄網(wǎng)站作為缺陷條目入口, 在4年的時間內(nèi)監(jiān)控其安全報告的情況, 通過人工的方式記錄缺陷相關(guān)提交(commit)與修復相關(guān)提交. 截至2019年, 該數(shù)據(jù)集共記錄了624個缺陷的修復, 涉及1 282個提交, 跨越205個Java項目. 該數(shù)據(jù)集已開源(https://github.com/SAP/vulnerability-assessment-kb/tree/master/MSR2019).
當前, 從NVD等缺陷庫中抽取缺陷條目已成為較為通用的做法, 其已經(jīng)成為構(gòu)建缺陷代碼數(shù)據(jù)集的重要數(shù)據(jù)來源. 這就意味著源缺陷庫的質(zhì)量會極大地影響所構(gòu)建數(shù)據(jù)集的質(zhì)量. 事實上, 通過細致的比對, 人們逐漸注意到該類缺陷庫的信息質(zhì)量存在一些問題. 例如, 在多個缺陷來源的對比中(如NVD與CVE)會出現(xiàn)缺陷信息不一致的情況. 如果對缺陷庫中的條目不加驗證即作為標答(當今普遍做法), 會給缺陷數(shù)據(jù)集的構(gòu)建帶來噪聲, 繼而極大地影響在此基礎(chǔ)上訓練的深度學習模型的檢測性能. 為此, Dong等人[56]提出了一個自動化系統(tǒng)VIEM來檢測完全標準化的NVD數(shù)據(jù)庫與非結(jié)構(gòu)化CVE描述及其引用的漏洞報告之間的不一致信息.其使用了自然語言處理任務(wù)中的命名實體識別與關(guān)系抽取技術(shù), 對非結(jié)構(gòu)化信息進行提取并與結(jié)構(gòu)化信息比較, 從而大規(guī)模地自動檢測不一致信息. 其對過去20年中78 296個CVE ID與70 569條缺陷報告進行了比對, 只有59.82%的條目可以嚴格匹配.
然而, 該不一致性的鑒別只針對缺陷影響的項目版本不一致性. 事實上, 由于公開缺陷庫都是由人工的方式進行維護, 其許多信息都可能存在錯誤/不一致, 例如對應(yīng)的原提交鏈接等. 這些都會給以此為基礎(chǔ)構(gòu)造的缺陷代碼數(shù)據(jù)集帶來影響, 也需要進行正確性的確認.
除了數(shù)據(jù)的沖突, 另一種影響數(shù)據(jù)集數(shù)量的問題是數(shù)據(jù)的缺失. 當前, 由于需要大量的缺陷代碼數(shù)據(jù), 使用混合數(shù)據(jù)來源已成為一種常用的擴增數(shù)據(jù)集的做法. 但是, 混合數(shù)據(jù)來源容易帶來的問題是屬性值的不全, 這是由各個缺陷庫的數(shù)據(jù)存儲結(jié)構(gòu)所導致的. 即使是同一數(shù)據(jù)源的缺陷數(shù)據(jù), 也可能有數(shù)據(jù)不全的情況. 例如, 在NVD缺陷庫中, 有些條目沒有CVE缺陷類型. 這種數(shù)據(jù)的缺失會影響數(shù)據(jù)集的數(shù)量與質(zhì)量, 需要對其進行補全.
Rostami等人[57]針對缺陷代碼數(shù)據(jù)中數(shù)據(jù)缺失的問題設(shè)計了一個機器學習框架, 從數(shù)據(jù)完整的條目中學習, 從而對缺失項進行預測. 該方法能夠較好地預測缺失的類型數(shù)據(jù), 但對于無類型數(shù)據(jù), 該框架無法補全.
Gonzalez等人[58]同樣針對NVD數(shù)據(jù)集中分類信息補全標注的問題, 提出了一個自動缺陷標注的方法. 其僅僅使用缺陷條目的CVE描述作為依據(jù), 在對描述信息進行清洗并使用TF-IDF表示后, 使用6種分類器(樸素貝葉斯、決策樹、支持向量機、隨機森林、AdaBoost支持向量機、多數(shù)投票), 按照NVD漏洞描述規(guī)則(vulnerability description ontology)中的19個類型標準來對其進行分類. 實驗結(jié)果顯示: 多數(shù)投票的分類效果最佳, 準確率達到74.5%. 但考慮到訓練成本和時間, 使用支持向量機是該任務(wù)的最優(yōu)選擇. 不過, 該方法同樣無法對選擇型數(shù)據(jù)進行補全.
因此, 對于非選擇型數(shù)據(jù)的缺失, 如何對其加以補全而不是簡單丟棄, 是構(gòu)造一個樣本數(shù)量足夠多的數(shù)據(jù)集的可行方向, 一些生成式的方法值得探究.
總的來說, 基于缺陷庫抽取的方法利用了現(xiàn)有的人工審查后的缺陷資源, 因此在缺陷來源上可靠性較高.使用該類方法構(gòu)造的缺陷代碼數(shù)據(jù)集, 其主要針對的是具有CVE編碼的真實軟件缺陷, 可以認為其樣本較為貼近缺陷檢測理想的使用場景. 但是, 由于缺陷庫需要相關(guān)人員的審查, 因此即使經(jīng)過數(shù)十年的積累(如NVD), 現(xiàn)有的缺陷庫中可被用于抽取樣本的缺陷條目數(shù)量仍然極為有限.
2.1.3
基于開源代碼庫爬取
從缺陷庫抽取缺陷數(shù)據(jù)較為可靠, 但缺陷庫中的可用數(shù)據(jù)量較少. Bhandari等人[59]的實證研究表明, NVD代碼庫中的171 178條缺陷數(shù)據(jù)中, 只有5 300余條能夠鏈接到對應(yīng)的提交代碼, 對齊率不足3.2%. 而GitHub等開源代碼庫中擁有海量的代碼提交, 其中擁有大量的缺陷提交與修復提交, 這些均可被挖掘作為缺陷條目. 由于人工成本等原因, 很多缺陷相關(guān)提交并未與CVE關(guān)聯(lián), 甚至沒有顯式的文字描述信息. 因此, 如何判別缺陷相關(guān)提交、如何挖掘代碼庫中的非標記缺陷條目, 成為了擴增缺陷代碼數(shù)據(jù)集的重要方向.
一種自動獲取缺陷數(shù)據(jù)的路線是啟發(fā)式搜索.
Karampatsis等人[60]聚焦于在單語句中可完成修改的缺陷, 構(gòu)造了針對Java語言的單語句缺陷代碼數(shù)據(jù)集ManySStuBs4J. 其選定最熱門的1 000個開源Java Maven項目, 從其GitHub倉庫中爬取歷史提交, 通過安全相關(guān)關(guān)鍵詞(error、bug、fix等)對其進行篩選, 并只保留在單語句中對缺陷進行修復的提交. ManySStuBs4J數(shù)據(jù)集最終包含153 652條單語句修復缺陷. 該數(shù)據(jù)集已開源(https://doi.org/10.5281/zenodo.3653444).
為了測試對跨函數(shù)缺陷的檢測效果, Li等人[61]從開源網(wǎng)站挖掘了一個函數(shù)級Java語言缺陷代碼數(shù)據(jù)集.其使用關(guān)鍵詞匹配的啟發(fā)式方法從8個著名開源Java項目的倉庫中爬取缺陷修復報告, 將修復前后的整個項目下載, 通過缺陷修復時的添加與刪除操作定位缺陷函數(shù). 不過, 其并未對標簽的情況進行人工驗證. 最終, 該數(shù)據(jù)集包含497萬個函數(shù), 其中, 182萬個為缺陷函數(shù), 占比36.7%. 雖然該數(shù)據(jù)集被用來測試跨函數(shù)的檢測效果, 但其在樣本的標注中只對缺陷函數(shù)進行了標注, 而并未將其相關(guān)函數(shù)也生成標注. 該數(shù)據(jù)集已開源(https://github.com/OOPSLA-2019-BugDetection/OOPSLA-2019-BugDetection).
啟發(fā)式搜索的規(guī)則較為簡單, 因此準確性難以得到保障, 可能會為數(shù)據(jù)集帶來較多噪聲. 為此, 研究者們開始探究通過學習的方式進行更精確的判斷與挖掘.
Zhou等人[62]分別訓練了6個基礎(chǔ)分類器(隨機森林、樸素貝葉斯、K近鄰等), 并通過邏輯回歸來對其分類結(jié)果進行整合, 從而自動判斷代碼提交或問題報告是否與缺陷相關(guān). 該系統(tǒng)僅對提交的自然語言部分進行分類, 而不考慮代碼部分的信息. 該系統(tǒng)可以實時監(jiān)控項目提交, 并不斷向數(shù)據(jù)集中補充樣本. 其從GitHub、JIRA、Bugzilla中挖取Java、Python、Ruby、JavaScript、Objective C和Go語言共計8 546個項目的提交信息與問題報告. 最終, 其提交的數(shù)據(jù)集中包含12 409個提交, 其中, 1 303個為缺陷提交, 占比10.5%. 問題報告數(shù)據(jù)集中包含24 188個問題報告, 其中, 1 905個為缺陷報告, 占比7.9%. 其安全團隊通過兩輪的標注對數(shù)據(jù)標簽進行驗證. 不過, 該數(shù)據(jù)集未開源.
Sabetta等人[63]也嘗試自動判別代碼提交以篩選出安全相關(guān)提交, 其在考慮自然語言信息的基礎(chǔ)上, 也將源代碼的修改加入?yún)⒖? 其構(gòu)建了兩個分類器, 分別依靠提交中的自然語言信息與代碼修改信息進行安全判斷. 對于提交日志信息, 其使用詞袋模型對其進行編碼, 使用支持向量機進行分類; 對于代碼修改信息, 其將源代碼修改視為自然語言中的文本撰寫, 因此直接使用標準的文本分類方法對其進行操作. 其只使用代碼中的標識符名稱序列來表示代碼, 同樣使用詞袋模型加以表示, 并使用支持向量機進行分類. 實驗結(jié)果顯示: 該方法對安全相關(guān)提交的分類精確度有所提高, 但F1值為0.64, 還有較大空間.
Wang等人[64]提出了一種自動的數(shù)據(jù)挖掘與標注流程. 該數(shù)據(jù)集從Sard、NVD及GitHub中挖掘缺陷相關(guān)提交(commit). 對于GitHub中的無標簽數(shù)據(jù), 其首先挑選高星值的項目爬取只修改單個文件的提交, 然后使用5種機器學習分類器進行共同投票, 包括SVM、LR、KNN、RF、GB, 以判斷該提交是否引入了缺陷. 這些分類器是用3 000個人工標記的提交訓練的. 為了保證分類器判斷的準確性, 5個分類器投票結(jié)果高度一致的提交才會被保留. 最終, 這些提交會按照函數(shù)級進行分割. 該數(shù)據(jù)集最終獲得了150 950條函數(shù)級的代碼樣本, 語言涵蓋C、Java、PHP和Swift, CWE缺陷類型達到30種. 該數(shù)據(jù)集已開源(https://github.com/HuantWang/FUNDED_NISL).
上述部分研究使用了機器學習方法進行自動數(shù)據(jù)挖掘, 鑒于使用深度學習進行文本分類已經(jīng)取得了較好的效果, 使用深度學習對代碼庫中的提交進行自動判斷也是一個值得探究的研究點.
此外, 也有通過人工方式從開源代碼庫中挖掘缺陷數(shù)據(jù).
Zhou等人[65]為了獲得更加精確標注的缺陷代碼數(shù)據(jù)集, 使用了純?nèi)斯俗⒌姆绞? 他們雇傭了一批安全人員對開源C/C++項目Linux Kernel、QEMU、Wireshard和FFmpeg進行標注. 首先對關(guān)鍵詞初篩后的安全相關(guān)提交(commit)進行人工判斷是否與缺陷修復相關(guān), 然后將這些提交分割為函數(shù)級別的代碼段. 該數(shù)據(jù)集共使用4名安全研究員, 耗時600工時來完成兩輪的標注與交叉驗證. 最終, 該數(shù)據(jù)集共收納58 965個樣本函數(shù), 其中, 缺陷相關(guān)函數(shù)為27 652條, 占比46.9%, 正負樣本相對均衡. 該數(shù)據(jù)集目前開源了2個工程(FFmpeg與Qemu).
Cheng等人[66]構(gòu)建了一個混合來源的缺陷代碼數(shù)據(jù)集, 其從Sard缺陷庫與2個開源軟件(lua、redis)中抽取缺陷條目. 對于Sard數(shù)據(jù)集, 其只選取最常見缺陷類型TOP 10的條目. 對于開源項目, 其通過管檢測檢索安全相關(guān)提交后再使用人工審查進行打標, 只選取缺陷修復提交前后的版本作為樣本, 該標注工作花費3人共720小時. 在收集到函數(shù)級與文件級數(shù)據(jù)后, 根據(jù)系統(tǒng)API調(diào)用作為起點進行前后向切片生成. 切片標簽由其是否包含缺陷相關(guān)語句決定, 即: 若切片包含至少一條缺陷語句, 則將其標記為“缺陷”. 在開源數(shù)據(jù)中, “缺陷語句”是根據(jù)修復提交的語句操作來判斷的. 為了方便與函數(shù)級缺陷檢測模型進行對比, 所有數(shù)據(jù)還按照函數(shù)級進行了上述標記. 最終, 該數(shù)據(jù)集包含140 670個切片, 其中, 缺陷切片為44 521個, 占比31.6%. 此外, 該數(shù)據(jù)集中Sard部分占比較大, 為98.3%. 該數(shù)據(jù)集已開源(https://github.com/DeepWukong/DeepWukong).
相對于自動方法, 通過人工方式挖掘需要大量的人力成本, 且其標注效率有限.
總的來說, 基于開源代碼庫爬取的方法可以得到大量的備選缺陷樣本, 但是爬取出的樣本是否為缺陷以及是哪種類型的缺陷都較難判斷: 自動化的方法正確率難以保障, 而人工判斷的方法則在效率上制約了數(shù)據(jù)集的大小. 此外, 由于爬取代碼庫時大多采用啟發(fā)式的方法, 其爬取的缺陷樣本在形態(tài)上具有不確定性: 既可能包含具有CVE編號的真實、復雜缺陷, 也可能包含項目普通缺陷甚至簡單編碼錯誤. 這種缺陷樣本的多樣性, 會對深度模型的設(shè)計提出較高的要求.
2.1.4
基于靜態(tài)分析工具生成
還有一種以相對低的成本快速獲取大量缺陷數(shù)據(jù)的方法, 即利用靜態(tài)分析工具對代碼進行缺陷檢測, 從而得到缺陷樣本.
Russell等人[67]從Juliet、Debian Linux和GitHub中選擇了共12 874 380個C/C++函數(shù)段, 經(jīng)過去重篩選后, 對無標簽的函數(shù)段(Juliet帶有標簽)使用3種開源靜態(tài)分析工具Clang、Cppchecke和Flawfinder進行缺陷檢測. 這3種分析工具擁有不同的檢測粒度, 通過投票的方式來共同對代碼段進行是否含有缺陷的標注. 最后, 通過人工的方式將分析工具報出的缺陷與CWE類型一一匹配對應(yīng). 最終, 該函數(shù)級缺陷代碼數(shù)據(jù)集包含1 286 262條樣本, 其中, 缺陷樣本為87 804條, 占比6.8%, 其類型對應(yīng)到CWE中有149個類型. 不過, 該數(shù)據(jù)集并未開源.
Dam等人[68]構(gòu)建了一個文件級的缺陷代碼數(shù)據(jù)集. 他們使用靜態(tài)分析工具(由于保密協(xié)議未透露)對開源項目Tizen進行了檢測, 并只選取資源泄露相關(guān)警告. 該數(shù)據(jù)集基于C語言, 最終包含8 118個源文件, 其中, 2 887個為有缺陷文件, 達到35.6%. 不過, 該數(shù)據(jù)集并未開源.
總的來說, 使用靜態(tài)分析工具可以利用其分析能力生成較大數(shù)據(jù)量的標注數(shù)據(jù). 但是靜態(tài)分析工具判別能力有限, 其誤報率高等缺陷在實踐中已被廣泛證實, 因此, 使用靜態(tài)分析工具得到的判別結(jié)果作為標準答案在可靠性上沒有保證. 此外, 使用靜態(tài)分析工具構(gòu)造樣本, 其數(shù)據(jù)集的表征能力上界即為分析工具的檢測能力, 在此數(shù)據(jù)集上訓練出的模型, 其效果只會低于直接使用分析工具, 因此, 使用深度模型意義不大. 而且, 由于當前靜態(tài)分析工具大多關(guān)注相對(CVE)簡單的缺陷類型和代碼編碼規(guī)范, 因此由其構(gòu)造的數(shù)據(jù)集價值相對于真實工業(yè)缺陷來說偏低.
2.2
當前數(shù)據(jù)集構(gòu)造方法存在的問題
為了便于對比, 我們對第2.1節(jié)中梳理的數(shù)據(jù)集構(gòu)造方法進行列表展示. 對于每一種數(shù)據(jù)來源, 挑選至多3項代表性工作, 詳細列舉其各項統(tǒng)計信息, 包括其缺陷數(shù)據(jù)來源、擁有的缺陷類型(其中, “?”表示未區(qū)分類型)、數(shù)據(jù)樣本的粒度、數(shù)據(jù)集總樣本數(shù)、缺陷樣本數(shù)、缺陷樣本占比以及數(shù)據(jù)集是否開源.
統(tǒng)計結(jié)果見表 1, 可以看到: 當前的缺陷代碼數(shù)據(jù)集在各項技術(shù)環(huán)節(jié)均存在較大的差異, 如不同的語言、粒度等. 對于從代碼庫爬取的缺陷數(shù)據(jù)集, 普遍存在沒有缺陷類型這一問題. 對于當前絕大多數(shù)數(shù)據(jù)集, 均存在正負樣本不均衡的問題. 有接近半數(shù)的數(shù)據(jù)集未進行開源, 給其他研究者進行模型復現(xiàn)與橫向?qū)Ρ葞砹死щy.

表 1 數(shù)據(jù)集統(tǒng)計信息小結(jié)
此外, 我們在表 2中按照每個技術(shù)環(huán)節(jié)的技術(shù)路線列舉了至多3項代表性的工作, 并評估了每一類技術(shù)路線的優(yōu)缺點.

表 2 數(shù)據(jù)集構(gòu)造方法小結(jié)
從數(shù)據(jù)來源上看, 不同的數(shù)據(jù)來源意味著其樣本復雜程度的不同. 例如, 在缺陷庫抽取中廣泛使用的NVD抽取數(shù)據(jù)與人工構(gòu)造中廣泛使用的Sard測試集數(shù)據(jù): 前者來源于真實的工業(yè)代碼, 其復雜程度較高, 且模塊化程度高, 各組件之間的依賴關(guān)系與調(diào)用關(guān)系復雜; 后者來源于人工撰寫的測試用例, 其復雜程度低, 且主要針對缺陷的表示, 而大多忽略其真實的功能, 因此為獨立程序片段, 幾乎沒有組件之間的長程依賴與復雜的調(diào)用關(guān)系. 從實際應(yīng)用的角度看, 缺陷檢測系統(tǒng)需要具備對真實工業(yè)代碼的檢測能力, 即理想的缺陷代碼數(shù)據(jù)集應(yīng)該來源于NVD等工業(yè)代碼而非人工撰寫的測試用例. 但工業(yè)代碼缺陷數(shù)據(jù)的獲取難度與成本遠大于測試用例, 需要更加完善的樣本提取技術(shù)與大量不可避免的人工審查.
從數(shù)據(jù)粒度上看, 不同的數(shù)據(jù)粒度在完整度與冗余度上存在差距. 文件級數(shù)據(jù)可以包含較多路徑, 但不可避免地帶來大量的無關(guān)噪聲. 函數(shù)級數(shù)據(jù)在劃分時成本極低, 且在檢測過程中不需要對被測代碼進行分析(切割為函數(shù)即可). 然而, 函數(shù)級數(shù)據(jù)基于的樸素假設(shè)是: 缺陷發(fā)生在函數(shù)的范圍內(nèi). 這在大規(guī)模工業(yè)代碼中顯然是難以支持的, 特別是在模塊化的情況下, 數(shù)據(jù)的聲明與使用往往不存在于單一函數(shù)中. 切片級數(shù)據(jù)可以支持跨函數(shù)的缺陷路徑, 但切片操作需要在構(gòu)建數(shù)據(jù)集與檢測被測代碼時均對代碼進行數(shù)據(jù)流、控制流等分析, 對資源的需求較大, 時間成本也較高.
從標注方式上看, 不同的標注方式使得標簽的精度與獲取成本存在差異. 安全相關(guān)人員的人工標注能夠保證標簽的質(zhì)量, 但其獲取成本較高, 且標注速度較慢, 達到深度學習模型需要的數(shù)據(jù)規(guī)模需要長期的積累. 使用啟發(fā)式方法自動化地進行標注可以快速、大量地生成標注, 然而其標注精度難以達到理想的標注效果.
下面我們對一些具體問題進行闡述.
2.2.1
不包含完整缺陷路徑
許多數(shù)據(jù)集[31?34, 41, 45, 64?67]是函數(shù)級數(shù)據(jù)集, 或者是先選定函數(shù)再在其內(nèi)部進行切片操作. 但是對于函數(shù)的選擇往往是根據(jù)缺陷補丁位置(diff修改的函數(shù))來決定的, 而補丁位置并不一定與缺陷在同一函數(shù)中, 這樣會導致缺陷樣本不包含完整的觸發(fā)路徑. 例如圖 2-2所示, 該代碼示意圖包含2個函數(shù)A和B. 缺陷在函數(shù)B被觸發(fā), 但是由于缺陷修復被寫在了靠前的函數(shù)A, 即與函數(shù)B的觸發(fā)位置不在同一個函數(shù)中, 所以最終觸發(fā)缺陷的語句不包含在數(shù)據(jù)集中(只包含diff所在的函數(shù)). 因此, 若將函數(shù)A作為缺陷樣本, 則其不是一個完整的缺陷路徑. 這種情況在模塊化較為顯著的大型項目中更為突出.

圖 2-2 不包含完整缺陷路徑的代碼示意圖
若缺陷樣本不包含完整的缺陷流程, 特別是不包含最終的缺陷觸發(fā)位置, 則在此基礎(chǔ)上進行的特征挖掘?qū)⑹ヒ饬x. 因此, 當抽取缺陷樣本時, 應(yīng)該考慮到跨函數(shù)的問題, 而不應(yīng)直接將代碼范圍限定在函數(shù)內(nèi)部.
2.2.2
正負樣本設(shè)置不合理
觀察一些數(shù)據(jù)集的構(gòu)造方法我們發(fā)現(xiàn), 一些數(shù)據(jù)集在構(gòu)造正樣本(缺陷樣本)與負樣本(非缺陷樣本)時不是針對同一個操作. 例如[42]: 將diff所在的函數(shù)標記為缺陷函數(shù), 而其余的未被diff影響的函數(shù)標記為非缺陷函數(shù). 事實上, 每個函數(shù)的功能是不同的, 其包含的操作也不相同. 例如: 對于CWE-119類型下的缺陷函數(shù), 其往往是負責處理緩沖區(qū)事務(wù), 因此包含較多緩沖區(qū)操作, 而其他函數(shù)并不一定有(且大概率沒有)如此多緩沖區(qū)操作. 因此, 將CWE-119類型缺陷函數(shù)以外的函數(shù)標為非缺陷函數(shù), 可能會使得模型退化成對是否含有緩沖區(qū)操作或是否含有較多緩沖區(qū)操作的識別, 而不是對緩沖區(qū)操作是否正確的識別. 因此, 有必要將正負樣本限定在相同的操作行為中. 例如, 使用修改前后的代碼作為正負樣本, 就可以避免這類問題.
2.2.3
橫向比較問題
當前, 基于深度學習的缺陷檢測任務(wù)分為不同的粒度, 如文件級缺陷檢測、函數(shù)級缺陷檢測、切片級缺陷檢測. 對于針對不同粒度的檢測系統(tǒng), 存在無法橫向比較的問題, 原因是不同粒度的標答模式不好界定. 如圖 2-2所示, 若缺陷路徑由函數(shù)A到函數(shù)B, 那么對于函數(shù)級缺陷檢測, 需要單獨對函數(shù)A與函數(shù)B標注, 此時對兩者標注“缺陷”或“非缺陷”都是有爭議的.
3 基于深度學習的源代碼缺陷檢測模型
如果將代碼簡單認為是一個文本符號序列, 則使用深度學習模型對代碼段進行缺陷判斷類似于文本分類問題. 但是由于代碼本身的特性與缺陷檢測任務(wù)的特性, 其中包含許多技術(shù)難點. 因此, 本節(jié)將列舉一些主要的技術(shù)難點, 介紹當前針對這些難點的研究方法.
3.1
代碼表征
由于代碼具備各個維度的特征, 例如局部的文本共現(xiàn)特征與長程的數(shù)據(jù)、控制依賴特征, 因此, 如何對代碼進行表征成為了設(shè)計深度學習模型需首要考慮的問題. 當前的方法主要通過序列、樹和圖對代碼進行表征, 其常用的對應(yīng)網(wǎng)絡(luò)結(jié)構(gòu)如圖 3-1所示.

圖 3-1 代碼表征的分類與應(yīng)用模型
下面將依次對其相關(guān)研究進行介紹.
3.1.1
基于序列的表征
由于深度學習在自然語言處理上的成功, 研究者很容易想到將其方法應(yīng)用到代碼語言上, 即將代碼通過序列的方式進行表征.
針對現(xiàn)有漏洞檢測系統(tǒng)依賴人類專家定義特征、經(jīng)常出現(xiàn)高漏報的問題, Li等人[42]提出了VulDeePecker, 這是一個基于深度學習的漏洞檢測系統(tǒng). 在輸入上, 其使用代碼片段作為輸入粒度, 因此對于輸入代碼均需檢測危險函數(shù)調(diào)用作為入口并進行切片組合. VulDeePecker選擇了循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)模型, 并使用雙向長短期記憶(bi-LSTM)來解決梯度消失與前后向依賴問題, 因為程序函數(shù)調(diào)用的參數(shù)可能會受到較早或較晚語句的影響, 其使用Word2Vec將輸入的代碼片段轉(zhuǎn)化為向量表示投入Bi-LSTM網(wǎng)絡(luò)進行訓練. Bi-LSTM網(wǎng)絡(luò)并未作特殊設(shè)計. 實驗結(jié)果表明: VulDeePecker總體檢測的F1值達到90.5, 顯著高于使用傳統(tǒng)方法(基于模式與基于代碼相似度)的缺陷檢測系統(tǒng), 并且可以大幅降低誤報率. 不過, 該模型并未開源.
由于VulDeePecker只使用了Bi-LSTM, Li等人[44]進一步測試了多種模型構(gòu)架, 包括GRU、DBN、MLP等. 其最終確認雙向門控循環(huán)單元(bi-GRU)擁有更好的檢測效果. 基于Bi-GRU的SySeVR模型在4個真實的工程(Libav、Seamonkey、Thunderbird、Xen)上檢測出15個缺陷, 其中, 7個為尚未發(fā)覺的缺陷. 不過, 該模型并未開源.
Xu等人[30]首先對程序根據(jù)調(diào)用情況進行切片以保留相關(guān)語句, 然后通過正規(guī)化去除命名影響, 最后將其轉(zhuǎn)化為向量形式作為模型輸入. 在分類模型上, 其將CNN與RNN結(jié)合成一個網(wǎng)絡(luò), 稱為“Contextual LSTM”.具體來說, 其首先通過CNN來挖掘局部特征, 然后投入LSTM來挖掘前后的依賴特征. 實驗結(jié)果顯示: CLSTM在Sard數(shù)據(jù)集上, F1可高達0.97, 高于對比的CNN與LSTM模型, 但提高不顯著.
借鑒自然語言處理中的情感分析任務(wù), Russell等人[67]設(shè)計了一個基于表示學習的源代碼分類器. 其首先將源代碼按照詞法進行嵌入表示, 在特征提取階段, 通過多層的卷積來挖掘局部特征或通過循環(huán)迭代來捕捉前后的依賴關(guān)系. 隨后, 通過池化層和全連接層得到最終的函數(shù)表示. 由于表示學習已經(jīng)學習到了函數(shù)的特征, 因此最后對函數(shù)的缺陷檢測就可以通過將其表示直接投入隨機森林分類器即可完成. 實驗結(jié)果顯示: 使用表示學習連接隨機森林分類器的方法比單純使用詞袋模型連接隨機森林的效果提高較多, 其檢測效果也顯著高于選擇的3種靜態(tài)分析工具(Clang/Flawfinder/Cppchecker). 不過, 該模型并未開源.
Choi等人[39]借鑒了自動問答領(lǐng)域的模式來判斷緩沖區(qū)調(diào)用是否安全. 其使用了記憶網(wǎng)絡(luò)的框架, 將緩沖區(qū)調(diào)用語句作為查詢語句, 將緩沖區(qū)相關(guān)的初始化與分配語句作為需要查詢的相關(guān)內(nèi)容, 最終幫助模型進行安全性的判斷. 具體來說, 其首先將代碼按照詞進行嵌入編碼, 將待查詢的上下文語句通過矩陣表示為記憶值模塊與記憶地址模塊, 再通過注意力機制, 使用查詢(緩沖區(qū)調(diào)用)語句得到相關(guān)記憶語句的排序, 最終通過檢索出的相關(guān)語句進行訪問操作是否安全的判斷. 實驗結(jié)果顯示: 該模型在4種復雜度級別的緩沖區(qū)操作的判斷中均高于傳統(tǒng)的CNN與LSTM模型, 在復雜度較高的情況下優(yōu)勢更加顯著. 不過, 該模型并未開源.
事實上, 代碼中包含了許多自然語言上的信息, 例如變量和函數(shù)的命名等. 這些信息可以指示該變量或函數(shù)的功能與屬性, 這種提示信息在擁有標準命名規(guī)則的大型工程中尤為明顯. Pradel等人[37]就利用了這樣的信息, 依靠深度學習, 僅僅針對命名信息進行缺陷檢測. 其通過Word2Vec對命名進行了嵌入式的向量表示, 以便模型挖掘其語義上的信息. 在分類模型上, 其選用了最基礎(chǔ)的前饋神經(jīng)網(wǎng)絡(luò), 沒有進行特殊的設(shè)計. 實驗結(jié)果顯示, DeepBugs模型在數(shù)據(jù)集上的檢測準確率超過89%. 在針對真實代碼的檢測中, 人工審查其150個缺陷報告后, 確認其中102個為正確的缺陷報出, 判斷精度達到68%, 證明了即使使用人工構(gòu)造的缺陷數(shù)據(jù)集也可以賦予模型挖掘真實缺陷的能力. 在效率上, 其單個文件的檢測時常不超過20 ms, 也顯示了其可用性.但是, 該模型的挖掘能力局限于生成缺陷代碼的變異模式, 而不具備較好的擴展性. 該模型現(xiàn)已開源(https://github.com/michaelpradel/DeepBugs).
Saccente等人[33]提出了原型工具Project Achilles, 其使用LSTM網(wǎng)絡(luò)對Java代碼作函數(shù)級缺陷檢測. 不同于其他方法使用單一模型對待測樣本進行判斷, Project Achilles針對其訓練數(shù)據(jù)中的每一個類型都訓練了一個基于LSTM的分類器, 因此會對待測樣本是否是某個類型的缺陷都進行一次評估. 這樣, Project Achilles不僅可以判斷待測樣本是否有缺陷, 同時也能直接得到該缺陷的類型. Project Achilles按token序列來讀入函數(shù)代碼, 在LSTM模型上, 其并未作特殊修改. 盡管一些類型只有100多個文件的數(shù)據(jù)作為訓練樣本, Project Achilles仍然在29個CWE類型中的24個上達到了超過90%的準確率. 該模型已開源(https://gitlab.com/TUSoftwareEngineering/vulnerabilitylocalization-using-machine-learning).
除了傳統(tǒng)的分類模型, 有些研究還嘗試了新的模型架構(gòu), 例如使用seq2seq的方法進行缺陷檢測.
為了只使用缺陷代碼和其對應(yīng)的修改后代碼進行缺陷檢測, Grag等人[69]提出了一個基于機器翻譯模型的檢測系統(tǒng). 其將缺陷代碼視為源語言, 將其修復版本視為目標語言, 通過基于RNN的編碼器與解碼器來學習其中的差異與轉(zhuǎn)化方式. 同時, 還使用修復代碼作為源語言, 同一修復代碼作為目標語言, 來使模型不對正確的代碼做轉(zhuǎn)化. 最終在檢測時, 若翻譯模型未對輸入代碼進行修改, 則認定其為無缺陷的正確代碼. 實驗結(jié)果顯示: 該方法在無噪聲和有噪聲的情況下檢測效果均好于傳統(tǒng)特征挖掘模型, 在有噪聲條件下效果更顯著. 不過, 其未與深度學習模型進行比較, 也未開源.
傳統(tǒng)的缺陷檢測方法針對不同的缺陷類型預設(shè)了不同的缺陷模式, 通過匹配的方式, 在判斷代碼是否含有缺陷的同時, 也可以得到其所屬的缺陷類型信息. 而基于深度學習的缺陷檢測系統(tǒng)往往只對代碼做二分類, 即判斷其是否含有缺陷, 而無法提供其缺陷類型, 這給開發(fā)人員帶來了一些不便因素.
為此, Zou等人[43]提出了第一個多分類的深度學習缺陷檢測系統(tǒng)μVulDeePecker. 該系統(tǒng)定義了代碼注意力, 即: 在輸入的代碼片段中選定符合缺陷語法特征規(guī)則的語句子集, 從而更好地挖掘缺陷的局部特征. 該缺陷語法特征規(guī)則是通過人工總結(jié)得到的, 包括庫/API函數(shù)調(diào)用中參數(shù)的定義語句、控制語句及包含庫/API函數(shù)調(diào)用的語句. 其將代碼片段與對應(yīng)的代碼注意力片段分別編碼并向量化, 同時作為該段代碼的表征投入Bi-LSTM進行多分類任務(wù)的訓練. 實驗結(jié)果顯示: μVulDeePecker模型在多分類任務(wù)上的效果好于單獨將VulDeePecker模型修改為多分類的版本, 在個別類型上效果尤為顯著. 引入的控制依賴對模型有較大的性能提升. 該模型未開源.
近年來, 大規(guī)模預訓練語言模型(如BERT[70])在自然語言領(lǐng)域展現(xiàn)出的優(yōu)良效果為程序語言研究者們提供了一種新的程序表示思路, 人們也開始探究基于Transformer在程序語言上構(gòu)建預訓練模型, 其中部分研究將缺陷檢測作為下游任務(wù)進行了初步的探究.
考慮到相關(guān)人員編寫代碼時會通過附加自然語言的注解與其他人員溝通, 一些研究者試圖利用自然語言來幫助更好地挖掘與建模程序語言. Feng等人[71]提出了CodeBERT, 該預訓練模型使用程序語言與自然語言共同預訓練. 其使用了兩個目標任務(wù)進行訓練, 分別為掩碼語言建模和替換令牌檢測. 為了測試代碼預訓練模型在非訓練任務(wù)的可用性, Zhou等人[72]將CodeBERT在實時缺陷檢測任務(wù)上進行了實驗(值得說明的是: 實時缺陷檢測任務(wù)同時將提交代碼與提交的自然語言信息作為輸入來判斷該提交是否引入了缺陷, 與傳統(tǒng)的缺陷檢測任務(wù)只輸入代碼存在一定差異). 實驗結(jié)果表明, 簡單地將CodeBERT替換原模型的編碼器即可接近SOTA的性能, 這表明了代碼預訓練模型的語義捕獲能力沒有局限在其訓練任務(wù)中. 該實驗已開源(https://github.com/Xin-Zhou-smu/Assessing-generalizability-of-CodeBERT).
不過, 實時缺陷檢測任務(wù)雖然與源代碼缺陷檢測任務(wù)同為判斷代碼是否有缺陷, 但兩者的輸入存在差異.因此, 代碼預訓練語言模型在缺陷檢測任務(wù)上的效果還需要進一步地實驗證實.
Ahmad等人[73]提出了一種能夠執(zhí)行廣泛的程序-自然語言的理解-生成任務(wù)(program and language understanding and generation)的預訓練模型PLBART, 其通過去噪自動編碼對大量Java和Python函數(shù)以及相關(guān)的自然語言文本進行了預訓練. 具體來說, 其在StackOverflow上抓取大量問題、答案與代碼段, 投入與BART[74]相同的結(jié)構(gòu), 并引入了3種噪聲: 令牌掩碼、令牌刪除與令牌填充使得模型能夠推理語言語法和語義, 并同時學習連貫地生成語言. 為了檢測PLBART對未見代碼語言的理解能力, 其在基于C/C++語言的缺陷檢測任務(wù)上進行了測試. 但是, 只對比了其他的語言模型, 并未與針對缺陷檢測任務(wù)的深度學習模型進行比較. Ahmad等人表示: 當前, 基于圖的缺陷檢測模型效果最好, 使用缺陷檢測任務(wù)只是用來探究預訓練語言模型在未見任務(wù)與未見語言上對程序語義的性能. 該模型已開源(https://github.com/wasiahmad/PLBART).
由此可見, 預訓練代碼語言模型在挖掘程序語義上具備一定的潛力, 但當前的研究還未統(tǒng)一在源代碼缺陷檢測的場景下橫向?qū)Ρ壬疃葘W習模型與預訓練語言模型的性能差異, 未來還需要進一步地深入探究. 總的來說, 基于序列的表征較為直觀, 且不需要對待測代碼進行額外的分析處理, 在實現(xiàn)上較為簡便. 但是不同于自然語言, 代碼語言具有更強的結(jié)構(gòu)性與局部性, 其上下文的依賴關(guān)系更為復雜且距離更長, 因此, 單純地使用序列作為代碼表征會損失大量的代碼結(jié)構(gòu)特征. 由于基于序列的表征存在的這類問題, 后期研究大多不再使用序列作為表征方式.
3.1.2
基于樹的表征
在對代碼進行分析時, 除了以序列的形式以外, 最常用的就是抽象語法樹(AST)的形式.
Li等人[53]提出了基于卷積神經(jīng)網(wǎng)絡(luò)的DP-CNN模型. 該模型針對文件級缺陷檢測, 并將抽象語法樹作為代碼原始表征以獲取更多的結(jié)構(gòu)信息. 其首先將文件代碼解析為抽象語法樹, 然后從中選擇代表性節(jié)點構(gòu)成表示該文件的向量, 轉(zhuǎn)化為詞嵌入形式后投入卷積神經(jīng)網(wǎng)絡(luò)模型進行特征的學習, 最終學習得到的表示特征與一些傳統(tǒng)特征混合投入邏輯回歸分類器進行分類. 在DP-CNN系統(tǒng)中, 其選擇的代表性節(jié)點是人工設(shè)定的, 包括方法調(diào)用和類實例創(chuàng)建的節(jié)點、聲明節(jié)點與控制流節(jié)點. 為了解決缺陷樣本少的問題, DP-CNN在訓練階段會重復使用缺陷樣本. 實驗結(jié)果顯示: 該方法在其數(shù)據(jù)集上的檢測高于使用深度置信網(wǎng)絡(luò)的方法, 但整體檢測性能有限, F1值僅為0.6. 該模型未開源.
Lin等人[41]使用了樹結(jié)構(gòu)作為代碼的表征. 具體地, 其使用代碼解析工具CodeSensor將函數(shù)代碼轉(zhuǎn)化為AST, 并使用深度優(yōu)先遍歷將其轉(zhuǎn)化為序列表示; 隨后進行截斷和補零使其成為等長語句; 最后, 使用Word2Vec將其轉(zhuǎn)化為向量形式. 在模型上, 該方法使用Bi-LSTM模型來捕獲缺陷特征, 模型結(jié)構(gòu)未作特殊修改. 在訓練完成后, 將具有少量標簽的目標代碼輸入訓練好的模型以獲得其表示并判斷缺陷. 實驗結(jié)果顯示, 使用表示學習的方法在TOP 10缺陷函數(shù)的檢測效果顯著優(yōu)于基于代碼度量的方法. 但是, 該模型的整體誤報率較高, 使其可用性不佳.
Dam等人[68]使用了基于樹結(jié)構(gòu)的LSTM網(wǎng)絡(luò)來對通過AST表征的代碼進行缺陷檢測, 具體地, 該模型首先將一個源文件清洗并解析為抽象語法樹, 每一個節(jié)點按照其類型名稱進行嵌入式表示, 將每一個節(jié)點都投入LSTM單元從而表征整個代碼文件, 最終通過傳統(tǒng)分類器(邏輯回歸與隨機森林)對整個源文件的AST特征向量表示進行缺陷判斷. 實驗結(jié)果顯示: 該模型在工程內(nèi)的數(shù)據(jù)集下的缺陷檢測F1值達到0.9以上, 但在跨工程的數(shù)據(jù)集下F1值只有0.5.
針對現(xiàn)有方法無法較好地處理跨函數(shù)缺陷的問題, Li等人[61]提出了一種結(jié)合使用上下文和注意力神經(jīng)網(wǎng)絡(luò)的方法. 其首先基于AST對函數(shù)代碼進行向量表征作為局部上下文, 再對AST上的路徑節(jié)點使用程序依賴圖和數(shù)據(jù)流作為全局上下文, 以將待測函數(shù)與可能導致錯誤代碼的其他相關(guān)函數(shù)連接起來. 通過增加對上下文的表示, 減少因為局部代碼相似度而導致的誤報. 同時, 為了避免上下文的引入使得代碼相似度匹配過于嚴苛, 其使用注意力機制來增加缺陷路徑的權(quán)重, 從而保證召回率. 在與基于規(guī)則的檢測工具和基于挖掘的方法比較的過程中, 該方法均大幅優(yōu)于對比方法, 但整體效果有限: 在難度相對低的項目內(nèi)檢測中, F1值為0.64. 該模型已開源(https://github.com/OOPSLA-2019-BugDetection/OOPSLA-2019-BugDetection).
在對代碼進行表示時, 傳統(tǒng)方法會使用定長向量, 這就需要對代碼內(nèi)容進行截斷(代碼過長)或填充(代碼過短), 這會帶來信息的丟失或冗余. Feng等人[34]提出了一種基于抽象語法樹的數(shù)據(jù)處理方法來提取所有句法特征并減少數(shù)據(jù)冗余, 其在雙向門控循環(huán)單元(bi-GRU)網(wǎng)絡(luò)上應(yīng)用包填充(pack-padded)方法來訓練可變長度數(shù)據(jù)而無需對變量截斷和填充. 具體來說, 其首先將程序源代碼解析為AST, 并按照函數(shù)節(jié)點進行切割, 得到按照函數(shù)分割的AST. 然后遍歷整個AST, 并將所有用戶定義的名稱映射到固定的預設(shè)名稱模式, 以消除不同命名帶來的差異. 為了能夠作為深度神經(jīng)網(wǎng)絡(luò)的輸入, 其通過前序遍歷將AST轉(zhuǎn)換為節(jié)點序列, 并使用Word2Vec將節(jié)點序列映射為向量表示. 在網(wǎng)絡(luò)模型上, 該方法使用雙向門控循環(huán)單元來應(yīng)對函數(shù)級表示帶來的長向量. 其在模型結(jié)構(gòu)上沒有特殊改動, 所不同的是, 其使用了包填充方法來應(yīng)對可變長度向量, 即針對不同的輸入長度進行記錄并同時輸入模型, 而不需要將向量長度統(tǒng)一化. 實驗結(jié)果顯示, 該模型在3種數(shù)據(jù)集劃分下均達到了0.82以上的F1值. 相對于開源分析工具Rats和Flawfinder, 該模型具有更高的準確率, 同時誤報率也更低. 不過, 該模型未開源.
總的來說, 相較于基于序列的表征方式, 基于樹的表征可以更好地顯式表征代碼中的結(jié)構(gòu)信息, 在傳統(tǒng)缺陷檢測方法中也大多會選用抽象語法樹作為分析對象. 但事實上, 除了抽象語法樹能夠表示的結(jié)構(gòu)信息以外, 源代碼本身還具備數(shù)據(jù)流、控制流等多種不同維度的特征, 而這些特征在缺陷檢測的過程中恰恰是較為關(guān)鍵的部分. 因此, 單純地使用抽象語法樹作為代碼表征是不夠的.
3.1.3
基于圖的表征
基于序列或樹的缺陷檢測在學習全面的程序語義以表征真實源代碼漏洞高度的多樣性和復雜性方面具有很大的局限性. 程序語言不同于自然語言, 其更具有結(jié)構(gòu)性與層次性, 并且具有抽象語法樹、數(shù)據(jù)流、控制流等多種不同維度的表示模式. 根據(jù)缺陷類型的不同, 需要考慮的維度也不同, 單純地使用平面序列或語法樹來表征代碼會嚴重限制模型覆蓋各種缺陷的能力.
由于圖神經(jīng)網(wǎng)絡(luò)與代碼的各種屬性圖可以較好地適配, 因此逐漸成為人們在缺陷檢測任務(wù)上的熱點模型. 其中, 如何構(gòu)建圖以建模表征代碼是最關(guān)鍵的探究點.
在代碼場景中, 缺陷代碼和非缺陷代碼有時相似度較高, 例如僅在單個保護數(shù)值或邊界條件上有細微差異, 而在其他操作上完全一致. 這類細小的差異往往難以被模型捕獲. 針對這個問題, Duan等人[31]提出了VulSniper模型, 其使用attention機制來捕獲代碼的關(guān)鍵部位. 具體來說, 其首先從源代碼生成程序?qū)傩詧D(code property graph)以盡可能地保留更多信息, 隨后將程序?qū)傩詧D轉(zhuǎn)化為一個144維的特征向量作為模型輸入, 并通過attention機制來計算不同節(jié)點的權(quán)重, 最后通過全連接層進行二分類判斷代碼是否有缺陷. 其中, attention部分分別使用多個一維卷積和一維卷積的轉(zhuǎn)置來實現(xiàn)自底向上和自頂向下的結(jié)構(gòu). 通過一維卷積, 感受野逐漸擴大以獲得周圍和全局信息. 通過一維卷積的轉(zhuǎn)置, 將高級特征縮放到與輸入相同的大小, 以便將注意力權(quán)重應(yīng)用于輸入. VulSniper針對的是函數(shù)級的缺陷檢測任務(wù), 其在Sard數(shù)據(jù)集中緩沖區(qū)錯誤(CWE-119)和資源管理(CWE-399)這兩個缺陷類型上的F1值達到了80.6%和73.3%, 遠超傳統(tǒng)方法. 不過, 該模型并未開源.
Zhou等人[65]提出的Devign模型基于AST來表征代碼, 并將不同層級的控制流、數(shù)據(jù)依賴、自然代碼序列顯式編碼為異構(gòu)邊的聯(lián)合圖, 每種類型表示與相應(yīng)表示相關(guān)的連接. 這種綜合表示方法有助于捕獲盡可能廣泛的漏洞類型和模式, 并能夠通過圖神經(jīng)網(wǎng)絡(luò)學習更好的節(jié)點表示. 在模型上, Devign使用帶有Conv模塊的門控圖神經(jīng)網(wǎng)絡(luò). 有別于使用全部節(jié)點進行分類, Conv模塊可以在學習到的豐富節(jié)點表示中提取有用的特征用于圖級分類. 實驗結(jié)果顯示: Devign在缺陷檢測任務(wù)的效果上顯著高于之前方法; 同時, 其在112個真實項目的缺陷函數(shù)中檢測準確率達到74%, 顯示了其在現(xiàn)實應(yīng)用上的可能性. 不過, 該模型并未開源.
Feng等人[75]同樣使用圖來表征程序代碼, 并通過GNN來對其進行分類, 并探究了不同模塊的不同設(shè)置帶來的影響. 其測試使用了不同屬性圖表征(AST、CFG、CPG)、不同編碼方式(Bag-Of-Words、Word2Vec、隨機)和不同GNN學習方法(DiffPool、Set2Set、DGCNN). 實驗初步結(jié)果顯示: AST+Word2Vec+DiffPool是當前效果最好的組合, 相較于靜態(tài)分析工具(Cppchecker、Clang、Flawfinder)有較大的性能優(yōu)勢.
Ghaffarian等人[35]設(shè)計了一種定制的程序中間圖表示, 其使用抽象語法樹(abstract syntax tree, AST)、控制流圖(control flow graph, CFG)和程序依賴圖(program dependence graph, PDG)作為信息來源. 在圖的基礎(chǔ)上, 對節(jié)點和邊的文本信息(變量名、類型)使用TF-IDF進行向量化. 在圖模型的選擇上, 其選用了圖卷積模型(graph convolutional network, GCN)與圖注意力模型(graph attention network, GAT), 其在網(wǎng)絡(luò)上沒有作特殊修改. 實驗結(jié)果顯示: 圖卷積模型在7個缺陷類型上的F1值都顯著高于其他方法, 但圖注意力模型在跨項目檢測上的效果更好. 同時, 相關(guān)實驗還指出: 對程序進行表征的向量程度不應(yīng)過小, 否則會影響圖神經(jīng)網(wǎng)絡(luò)的性能. 與一些研究會對代碼進行規(guī)范化處理(替代變量名等)不同, 該研究通過實驗特意指出, 規(guī)范化操作會影響模型辨別效果. 不過, 該研究的代碼并未開源.
傳統(tǒng)方法使用序列或無類型圖來表征代碼作為輸入, 這會丟失很多代碼結(jié)構(gòu)上的控制與依賴信息. Wang等人[64]提出了FUNDED, 它在抽象語法樹的基礎(chǔ)上, 人工定義了8種關(guān)系, 包括數(shù)據(jù)依賴關(guān)系、控制依賴關(guān)系、守護關(guān)系、跳轉(zhuǎn)關(guān)系、運算關(guān)系、序列鄰接關(guān)系、最后使用關(guān)系、最后詞法使用關(guān)系, 以將抽象語法樹擴增為一個有向多圖. FUNDED使用這9種關(guān)系(本身抽象語法樹的關(guān)系)得到的鄰接矩陣來表征整個函數(shù)代碼段, 從而使模型能夠獲得更多的語義、控制等信息. 該模型使用門控圖神經(jīng)網(wǎng)絡(luò)(GGNN)對表征函數(shù)的鄰接矩陣進行分類, 其在自建數(shù)據(jù)集上獲得了較好的效果, 在真實項目的檢測測試上性能也超過了之前的模型. 該模型已開源(https://github.com/HuantWang/FUNDED_NISL).
Cheng等人[66]也利用圖來對代碼片段進行表征, 其提出了DeepWukong. 具體地, 其首先生成代碼的控制依賴圖和數(shù)據(jù)依賴圖, 使用分析工具SVF從其中找到系統(tǒng)API調(diào)用作為關(guān)鍵節(jié)點并抽取切片, 使用Doc2Vec將清洗后的代碼轉(zhuǎn)化為向量形式, 將得到的切片圖向量表示投入3種圖卷積網(wǎng)絡(luò)(GCN、GAT、k-GNNs)進行分類. 通過圖的形式, 既保留了結(jié)構(gòu)信息(邊)也保留了文本信息(節(jié)點向量). 在人工數(shù)據(jù)和真實數(shù)據(jù)的部分實驗結(jié)果顯示: 不論使用哪種圖卷積網(wǎng)絡(luò), DeepWukong都能擁有較好的辨別性能. 該模型已開源(https://github.com/DeepWukong/DeepWukong).
總的來說, 基于圖的表征相對于序列或樹能夠顯式表征更多維度的代碼特征, 當前被認為是更具前景的代碼表征模式. 但基于圖的表征也存在一些問題, 例如在基于圖的表征模式下, 相同節(jié)點往往會被合并, 代碼語言的自然順序也會丟失, 這類信息丟失問題都是在構(gòu)建基于圖的表征時需要考慮的.
3.1.4
基于其他表征
除了采用序列、樹或圖的方式來對代碼進行表征以外, 對代碼進行其他形式的轉(zhuǎn)化也是一種思路.
Cao等人[32]提出了帶有傅里葉變換的深度卷積LSTM神經(jīng)網(wǎng)絡(luò)FTCLNet用于漏洞檢測. 具體地, 其使用離散傅里葉變換方法將代碼空間轉(zhuǎn)換為頻域, 并將卷積神經(jīng)網(wǎng)絡(luò)與長短期記憶網(wǎng)絡(luò)結(jié)合起來, 以捕捉頻域上的局部和全局特征, 再通過反向傅里葉變化將其轉(zhuǎn)化回代碼空間, 同時加入注意力機制來調(diào)整權(quán)重, 最后使用全連接層進行預測. 實驗結(jié)果顯示: FTCLNet的檢測效果高于VulDeePecker等深度方法, 并顯著高于Flawfinder等檢測工具.
對源代碼進行分析, 涉及到路徑可達等問題, 這對模型的求解能力提出了很高的要求, 也影響了模型的性能. 一個解決方案是通過編譯, 借助編譯器將模型轉(zhuǎn)化為匯編代碼, 再交給深度學習進行后續(xù)的缺陷判斷. Pechenkin等人[76]使用雙向LSTM對向量化的匯編代碼進行缺陷判別. 匯編代碼的操作更加細化, 但同時會導致更大的代碼量, 使得對長程依賴的代碼結(jié)構(gòu)更加難以捕捉.
一些語言特性會導致直接對源代碼進行缺陷檢測較為困難, 如C/C++中宏的跨文件使用. 這時, 利用編譯器對源代碼進行預處理可以減緩這些問題. Li等人[77]提出了VulDeeLocator, 其將源代碼轉(zhuǎn)化為中間代碼再進行缺陷檢測, 并使用粒度細化來縮小定位的范圍. VulDeeLocator讀入中間代碼的向量表征與定位缺陷的矩陣表示, 其在標準的Bi-GRU模型中增加了3層: 通過乘法層完成注意力獲取; 通過k-max池化層與平均池化層來完成粒度細化. 在200個隨機挑選的真實工程文件中, VulDeeLocator檢測出了18個缺陷, 其中16個為已報出缺陷, 2個為靜默修復缺陷. 不過, 該模型并未開源. 此外, 將代碼轉(zhuǎn)化為中間語言需要對代碼進行編譯, 這在大規(guī)模程序的應(yīng)用場景下成本較高, 其易用性低于直接針對源代碼進行操作. 該模型已開源(https://github.com/VulDeeLocator/VulDeeLocator).
3.2
可解釋性
雖然在實驗結(jié)果上, 基于深度學習的缺陷檢測已經(jīng)展現(xiàn)了較好的效果, 甚至能夠超過一些傳統(tǒng)的檢測方法, 但是深度學習方法只能提供結(jié)果, 而不能提供解釋性的指導. 相比之下, 基于領(lǐng)域?qū)<揖帉懙穆┒礄z測規(guī)則得到的檢測結(jié)果能夠更好地提供這些解釋. 此外, 解釋性的缺失也使得從業(yè)人員無法判斷基于深度學習的缺陷檢測器學到了什么知識. 因此, 深度學習可解釋性是一個重要的研究課題, 它可以幫助人類深入了解軟件漏洞的起因、檢測, 并對缺陷的確認與修復提供幫助.
An等人[78]認為: 將函數(shù)或文件作為缺陷粒度過于寬泛, 其包含了過多的冗余信息. 而且即使判斷了包含缺陷后, 也需要花費很多的人力去定位缺陷的具體位置. 因此, 切片才是合適的粒度, 且模型需要提供更多的可解釋性. 為此, 他們提出了AVDHRAM, 即基于層次表示和注意力機制的自動缺陷檢測模型. 其將程序分為了5個層次, 分別是程序、函數(shù)、切片、語句、字符. 通過與SySeVR同樣的切片起始點開始切片, 在經(jīng)過符號化與向量化后, 通過使用層次注意力網(wǎng)絡(luò)(hierarchical attention network)來學習通過字符表示語句與通過語句表示切片. 其中, 注意力機制還可以用來進行可視化的表示, 用以表明對模型判斷指導最多的部分, 從而輔助人工驗證. 實驗結(jié)果顯示: AVDHRAM的檢測效果顯著好于靜態(tài)分析工具, 與SySeVR性能相當.
Zou等人[79]也針對可解釋性作了一些初步的嘗試. 他們提出了一個可解釋框架, 以通過模型對代碼段的缺陷判斷抽取出判斷規(guī)則. 該框架的核心內(nèi)容是識別對做出特定預測貢獻最多的少量token, 通過這些token構(gòu)建一個決策樹規(guī)則, 從而幫助該領(lǐng)域人員的理解與判斷. 該框架應(yīng)用在VulDeePecker與SySeVR模型上的結(jié)果顯示: 該框架確實可以識別重要特征, 對人工驗證檢測器的結(jié)果起到了輔助的作用. 但是, 該解釋性框架也存在一些問題, 例如: 無法解釋為什么模型認為某些token的重要性更高; 生成的規(guī)則是半自動化總結(jié)的, 而不是完全自動化地進行解釋; 框架針對每一個特別的預測進行解釋規(guī)則的生成, 而不能生成適用于解釋其他示例的全局規(guī)則. 這些問題都是日后工作亟待解決的.
Mao等人[80]提出了一個基于注意力機制與雙向RNN的可解釋性缺陷檢測模型, 其首先將源代碼按照函數(shù)轉(zhuǎn)化為AST, 使用一個堆疊的雙向LSTM網(wǎng)絡(luò)來學習其表示, 再投入基于注意力機制的雙向RNN模型進行分類. 在NVD與Sard抽取出的數(shù)據(jù)集測試下, 其獲得了較好的分類效果, 但是對于可解釋性的效果, 作者并未提出定量的評估方法.
為了使模型能夠更精細地告知代碼中哪些部分是與缺陷相關(guān)的, Li等人[81]提出了一個基于圖網(wǎng)絡(luò)的可解釋的缺陷檢測器IVDetect (interpretable vulnerability detector), 用來豐富模型的輸出內(nèi)容. 傳統(tǒng)方法會將缺陷函數(shù)的整體作為輸入, 而IVDetect會對其中語句進行“缺陷相關(guān)”和“上下文”的區(qū)分. 其首先構(gòu)建缺陷判別模型FA-GCN, 將源代碼以程序依賴圖的形式輸入, 在編碼時加入其數(shù)據(jù)依賴與控制依賴上下文; 分類主體是用于圖分類的圖卷積網(wǎng)絡(luò)GCN, 并使用了特征注意力機制. IVDetect使用GNNExplainer來提供解釋性結(jié)果, 其將FA-GNN模型、其判斷結(jié)果和函數(shù)PDG作為輸入, 通過在PDG中尋找一個最小子圖, 使其在FA-GNN中得到的分數(shù)最接近原PDG. GNNExplainer通過對邊進行遮蓋并觀察模型判斷來達到對邊重要性的評估. 實驗結(jié)果顯示: 在函數(shù)級缺陷檢測上, IVDetect相對前人方法有大幅度的提升; 在缺陷相關(guān)語句的辨別中, IVDetect有67%可以在TOP 5列表中準確命中.
該模型已開源(https://github.com/vulnerabilitydetection/VulnerabilityDetectionResearch).
但是對于可解釋性問題, 最大的難點在于難以定量評估其解釋性效果. Li等人[81]使用了Reveal[26]、Fan[52]、Devign[65]的數(shù)據(jù)集, 均為函數(shù)級缺陷代碼數(shù)據(jù)集. 共包含197 551個函數(shù), 其中, 22 278個為缺陷函數(shù), 占比11.3%. 其中, Fan數(shù)據(jù)集中包括對缺陷修復的記錄, 因此被用于評估缺陷相關(guān)語句的判斷效果. 但是, 修復語句與缺陷相關(guān)語句并不等價, 簡單地將修復所影響的語句作為解釋性語句的標準答案, 合理性不高.
事實上, 相較于缺陷檢測中的標準答案, 可解釋性由于其具備一定的主觀因素, 其標準答案的制訂更加困難. 因此, 為了評估可解釋性的優(yōu)劣, 還需要一個能夠被廣泛接受的評價標準.
3.3
泛化能力
在缺陷檢測的過程中, 不同項目之間或不同語言之間的缺陷存在分布上的差異, 因此, 基于深度學習的方法需要使用域適應(yīng)學習的方法來使得模型具備跨項目與跨語言的泛化能力. 深度域適應(yīng)學習鼓勵模型學習源數(shù)據(jù)和目標數(shù)據(jù)的新表示, 以最大限度地縮小它們之間的差異. 源數(shù)據(jù)和目標數(shù)據(jù)通過生成器映射到聯(lián)合特征空間, 通過最小化其分布之間的差異, 使得兩者在聯(lián)合空間中得以更好地鏈接.
Nguyen等人[82]利用深度領(lǐng)域適應(yīng)來進行缺陷檢測, 提出了CDAN (code domain adaptation network)模型, 其使用序列代碼的向量表示作為輸入, 通過雙向RNN作為生成器來生成源數(shù)據(jù)和目標數(shù)據(jù)在聯(lián)合空間中的表示, 通過判別器對兩者表示進行區(qū)分, 以不斷地減少兩者表示的差距, 最終通過在有標簽源數(shù)據(jù)上訓練的分類器進行分類. 由于在納什均衡點, 源與目標在聯(lián)合空間中是相同的, 因此可以將訓練好的分類器對目標進行預測. 在CDAN基礎(chǔ)上, 其提出了半監(jiān)督的模型SCDAN (semi-supervised code domain adaptation network), 使用條件交叉熵和光譜圖使其滿足平滑假設(shè)和聚類假設(shè). 實驗結(jié)果顯示: 使用CDAN遷移框架, 能夠顯著提高模型在未標注數(shù)據(jù)上的預測性能.
當前, 深度學習模型在缺陷檢測任務(wù)方面能夠取得較好的效果, 但其訓練集和測試集是同分布的. 事實上, 當應(yīng)用到實際項目中時, 待測樣本往往與訓練樣本是不同分布的. 例如待測樣本和訓練樣本來源于不同的工程, 或者是不同的缺陷類型. 為此, Liu等人[48]提出了CD-VulD, 即跨域軟件缺陷檢測, 以解決缺陷預測模型的跨域遷移問題. 具體地, CD-VulD首先將程序轉(zhuǎn)化為token序列并轉(zhuǎn)化為數(shù)字型向量, 然后通過基于Bi-LSTM的深度特征模型來學習高層序列表示, 通過矩陣轉(zhuǎn)移學習框架來縮小源域和目標域的分布差異以學習跨域表示, 最終學到的跨域表示被用來訓練分類器進行分類. 該模型針對的是函數(shù)級缺陷檢測. 實驗結(jié)果顯示, 使用跨域?qū)W習得到的代碼表示能夠顯著提高機器學習和深度學習分類器的檢測效果. 在跨域問題上, CD-VulD在類型上的遷移和項目間的遷移都顯示出明顯優(yōu)勢, 這種優(yōu)勢在源域與目標域使用不同代碼表征方式時依然存在甚至更加突出. 該模型并未開源.
上述研究主要通過遷移方法提高項目間的泛化能力, 而語言之間的泛化與遷移同樣應(yīng)予以重視.
Hua等人[83]復現(xiàn)了針對C/C++語言的缺陷檢測系統(tǒng)VulDeePecker與SySeVR, 以測試其對Java代碼中相對較多的單語句缺陷的檢測效果. 其在單語句缺陷數(shù)據(jù)集ManySStuBs4J上的檢測準確度從原來的超過90%降低到70%. 這印證了單語句缺陷并不能很好地被通用缺陷檢測系統(tǒng)所探查.
事實上, 語言間的泛化是一個同樣重要且難度更大的研究點.
3.4
額外特征的挖掘
在缺陷檢測的任務(wù)背景下, 除了代碼段本身的特征, 還包含一些額外的特征, 例如代碼的傳統(tǒng)度量信息、缺陷的修復信息, 這些都可以為模型的特征挖掘提供指導.
缺陷代碼和非缺陷代碼的差異有時候很小, 有時甚至修改單個變量、符號甚至數(shù)值就可以改變代碼的正確性. 而基于相似度的缺陷復用檢測往往無法分辨該類細微的差異而產(chǎn)生誤報. 因此, 重點關(guān)注缺陷代碼及其修復是十分有意義的.
在機器學習的缺陷檢測研究中, Xiao等人[45]就將代碼的修復信息作為重要特征來挖掘. 他們提出了MVP模型. 首先對缺陷代碼及其對應(yīng)的修復版本生成抽象語法樹與代碼屬性圖, 根據(jù)補丁的修改情況, 使用不同策略進行切片, 以分離出缺陷相關(guān)的語法與語義特征, 然后通過待測代碼的特征與其比對, 若其匹配缺陷特征而不匹配修復特征, 則認為其有缺陷. MVP在10個開源工程上的檢測效果顯著高于基于克隆(ReDeBug、VUUDY)、基于函數(shù)匹配(SourcererCC、CCAligner)、基于深度學習(VulDeePecker、Devign)的檢測方法, 同時, 其性能高于商業(yè)分析工具(Coverity、Checkmarx), 擁有更低的誤報率和漏報率. 不過, 該模型并未開源.
Wu等人[84]同樣利用了修復信息來降低誤報率, 不同的是, 其只是簡單地通過相似度來進行計算, 若待測代碼和匹配缺陷的修復后版本相似度更高, 則認定該報出為誤報.
在深度學習的缺陷檢測研究中, 也應(yīng)該重點關(guān)注如何更好地利用缺陷的修復信息.
使用深度學習進行缺陷檢測, 并不一定要完全拋棄傳統(tǒng)方法, 也可以進行一定的結(jié)合.
Clemente等人[50]利用多層前饋神經(jīng)網(wǎng)絡(luò)來尋找適用于缺陷檢測的代碼度量組合. 其計算了代碼的3個維度的度量值, 包括復雜度度量、組成數(shù)值度量、面向?qū)ο蠖攘? 并輸入多層前饋神經(jīng)網(wǎng)絡(luò)來尋找合適的組合.實驗結(jié)果顯示: 使用深度網(wǎng)絡(luò)來結(jié)合傳統(tǒng)度量值是有效果的, 且其效果要好于使用機器學習(對比決策樹、隨機森林、支持向量機、樸素貝葉斯), 但提升效果有限.
傳統(tǒng)方法通過代碼的度量信息來進行缺陷預測, 但是相對于缺陷模式的多樣性, 通過度量進行表征還不夠充分. Zhang等人[54]提出了DefectLearner, 其在傳統(tǒng)代碼度量中加入了交叉熵(cross entropy)作為代碼特征, 一同投入深度網(wǎng)絡(luò)進行缺陷檢測. DefectLearner首先對代碼進行序列化的向量表示, 使用LSTM構(gòu)造語言模型來捕捉代碼模式與隱層特征, 最后加入標簽信息與交叉熵及度量特征來訓練分類器. 該模型使用了支持向量機、隨機森林、樸素貝葉斯與邏輯回歸這4種分類器. 實驗結(jié)果顯示: 交叉熵的判別效果可以高于一半的傳統(tǒng)度量值, 在加入交叉熵特征后, 能夠全面提高模型的檢測性能.
傳統(tǒng)的缺陷檢測方法在一定程度上是有效果的, 這些效果在實際的工業(yè)領(lǐng)域也已經(jīng)得到了驗證. 但是傳統(tǒng)方法往往針對的是不同的使用場景, 在不同類型上的檢測水平存在差異. 為此, Jabeen等人[85]提出了一個整合的缺陷檢測模型, 以對傳統(tǒng)檢測方法加以綜合考慮. 該模型選擇了4個傳統(tǒng)缺陷檢測模型, 將其檢測結(jié)果作為輸入, 投入多層感知機(MLP)以獲得整合后的缺陷預測結(jié)果. 其使用的是傳統(tǒng)的3層感知機(輸入層、隱層、輸出層), 沒有作特殊的修改. 在從CVE抽取出的真實用例作為數(shù)據(jù)集的實驗結(jié)果上看, 使用MLP進行整合能夠獲得最低的錯誤率.
就當前缺陷檢測商業(yè)軟件的發(fā)展情況來看, 絕大多數(shù)商業(yè)軟件還是基于傳統(tǒng)的規(guī)則方法. 這是由于基于規(guī)則的方法往往能夠提供完整的缺陷路徑, 對于其后續(xù)的確認更為方便, 且當前基于規(guī)則的方法檢測效果更為穩(wěn)定, 不需要考慮跨域遷移等問題. 這種商業(yè)現(xiàn)象顯示出了傳統(tǒng)方法在當今的缺陷檢測領(lǐng)域依然存在其價值. 因此, 如何將深度學習技術(shù)與傳統(tǒng)方法相結(jié)合, 是一個值得探究的研究點.
3.5
當前深度學習模型存在的問題
為了便于對比, 我們對第3節(jié)中梳理的深度學習模型進行列表展示, 對于每一種代碼表征形式挑選至多3項代表性工作, 詳細列舉其各項技術(shù)信息. 如表 3所示, 表中的每一行都代表一項研究工作. 第1列代表了該研究使用的代碼表征形式. 第3列為分析對象的編程語言. 第4列為分析對象的樣本粒度. 第5列為分析對象表征時選擇的特征, 包括抽象語法樹(AST)、控制流圖(CFG)、數(shù)據(jù)流圖(DFG)、程序依賴圖(PDG)、代碼屬性圖(CPG)、離散傅里葉變換(DFT)、編譯中間代碼(LLVM)等. 第6列為該深度模型使用的模型架構(gòu), 包括前饋神經(jīng)網(wǎng)絡(luò)(FNN)、卷積神經(jīng)網(wǎng)絡(luò)(CNN)、雙向長短期記憶(bi-LSTM)、雙向門控循環(huán)單元(bi-GRU)等. 第7列為該模型所使用的評價指標, F1(以及多分類下的W-F1)即指代包含誤報率(FPR)、漏報率(FNR)、召回率(TPR/recall)、精確率(P)和F1值的傳統(tǒng)評價體系, 其他指標還包括正確率(accuracy)等. 第8列標記其是否開源. 第9列表示該類表征的預處理難度.

表 3 深度學習模型統(tǒng)計信息小結(jié)
可以看到: 針對不同的語言, 研究者們已經(jīng)嘗試使用了主流的卷積、序列、圖模型進行缺陷檢測任務(wù), 主要檢測對象集中在主流編程語言. 但各模型針對的樣本粒度差異較大, 且只有個別模型為開源狀態(tài), 使得模型間的比較較為困難.
從代碼表征維度上看, 基于序列的表征較為直觀, 且不需要對待測代碼進行額外的分析處理, 在實現(xiàn)上較為簡便. 但是代碼語言具有較強的結(jié)構(gòu)性與局部性, 其上下文的依賴關(guān)系復雜且距離較長, 因此, 單純地使用序列作為代碼表征會損失大量的代碼結(jié)構(gòu)特征. 基于樹的表征可以更好地顯式表征代碼中的結(jié)構(gòu)信息, 但事實上, 除了抽象語法樹能夠表示的結(jié)構(gòu)信息以外, 源代碼本身還具備數(shù)據(jù)流、控制流等多種不同維度的特征, 而這些特征無法顯式地被抽象語法樹表示. 基于圖的表征能夠顯式表征更多形式與維度的代碼特征, 當前被認為是更具前景的代碼表征模式. 但基于圖的表征也存在一些問題, 例如: 在基于圖的表征模式下, 相同節(jié)點往往會被合并, 代碼語言的自然順序也會丟失; 其次, 大規(guī)模代碼檢測時的圖構(gòu)建需要耗費大量的計算資源與時間, 這些都是在使用基于圖的表征時需要考慮的.
除了代碼的表征, 在模型階段對數(shù)據(jù)集的劃分、使用與針對性設(shè)計是更加突出的問題.
3.5.1
多數(shù)據(jù)來源混用
在前述方法中, 許多研究[42?44]都使用了多來源的數(shù)據(jù)集, 作為訓練和評估的基礎(chǔ). 多來源數(shù)據(jù)集往往是人工構(gòu)造的數(shù)據(jù)集Sard與從真實項目中抽取的缺陷條目(NVD抽取), 而由于真實項目缺陷條目獲取成本較高, 這類混合數(shù)據(jù)集大多只包含少量的真實缺陷. 例如, VulDeePecker[42]的數(shù)據(jù)集中僅有1.4% (840個)來自于真實項目缺陷(NVD), 這種數(shù)據(jù)比例會導致基于大量人工構(gòu)造缺陷數(shù)據(jù)訓練的深度模型在真實缺陷上的檢測能力并沒有得到很好的評估.
事實上, 人工構(gòu)造缺陷在代碼長度與復雜度上都遠低于真實抽取的缺陷, 人工構(gòu)造缺陷上挖掘得到的特征可能并不適用于真實缺陷. 這需要更進一步的探究.
3.5.2
重復切片導致的數(shù)據(jù)泄露
表 4為VulDeePecker[42]構(gòu)建的數(shù)據(jù)集中的兩個樣本, 其切片代碼與標簽完全一致. 由于在一個切片中的不同位置作為起點進行切片, 可能會得到一樣的切片, 因此會導致數(shù)據(jù)重復. 而當前劃分數(shù)據(jù)集時大多使用隨機劃分, 會使得這些重復的數(shù)據(jù)分別被劃分到訓練集與測試集, 這就導致了大量的泄露, 使得模型的檢測效果虛高.

表 4 VulDeePecker[42]數(shù)據(jù)集中的兩個樣本
事實上, 從不同位置作切片生成相同的切片符合實際應(yīng)用的樣本分布. 因此, 解決該問題不應(yīng)從去重的角度, 而應(yīng)從訓練數(shù)據(jù)的劃分角度來考慮. 需要在劃分時避免泄露, 即避免相同的切片被分開, 即可規(guī)避重復切片帶來的虛假測試值.
3.5.3
缺乏數(shù)據(jù)不平衡的針對性設(shè)計
由于代碼中大部分代碼均為正確代碼, 因此在正常的構(gòu)造流程下, 缺陷代碼在其中的比例都較低. 例如在VulDeePecker[42]中, 缺陷代碼只占28.8%. 因此, 在這種條件下, 需要針對不平衡問題進行針對性的設(shè)計.
已有部分研究考慮到該問題, 并通過例如SMOTE算法進行改進. 不過, 該方向依然存在較大改進的空間, 例如可以探究使用數(shù)據(jù)增強的方式改善不平穩(wěn)問題.
3.5.4
評價指標不符合使用場景
當前, 絕大多數(shù)缺陷檢測系統(tǒng)均使用基于F1值的評價體系, 即分別計算準確率(accuracy)、精確率(precision)、召回率(recall), 最后通過F1值綜合評價模型性能. 一些研究也會使用真正例(TP)、假正例(FP)、假反例(FN)和真反例(TN)來評估. 這些均為較常用的評估指標, 這里不再贅述.
然而, F1值無法很好地評價真實使用場景. 在真實開發(fā)環(huán)境中, 人工審查往往精力有限, 不會審查全部報出缺陷, 而會根據(jù)系統(tǒng)預測的排序, 審查靠前的報出. 因此, 應(yīng)當加入TOP N限制下的各項指標來評估, 其更加符合實際的應(yīng)用場景.
4 未來研究展望
在對當前研究進展進行了歸納總結(jié)與問題闡述后, 我們對該任務(wù)未來的研究點進行展望.
4.1
構(gòu)建通用多粒度數(shù)據(jù)集
當前, 缺陷檢測領(lǐng)域沒有一個統(tǒng)一通用的評價標準與評價流程, 這使得不同模型之間的比對較為不便.
如前文所述, 當前研究均使用自行構(gòu)建的缺陷代碼數(shù)據(jù)集, 這些數(shù)據(jù)集在數(shù)據(jù)來源、數(shù)據(jù)粒度與標注方式上均存在較大的差異.
從數(shù)據(jù)來源上看, 不同的數(shù)據(jù)來源意味著其樣本復雜程度的不同. 缺陷庫抽取數(shù)據(jù)(如NVD)來源于真實的工業(yè)代碼, 其復雜程度較高, 且模塊化程度高, 各組件之間的依賴關(guān)系與調(diào)用關(guān)系復雜. 人工構(gòu)造數(shù)據(jù)(如Sard)來源于人工撰寫的測試用例, 其復雜程度低, 且主要針對缺陷的表示, 而大多忽略其真實的功能, 因此為獨立程序片段, 幾乎沒有組件之間的長程依賴與復雜的調(diào)用關(guān)系. 在這兩類不同復雜度的數(shù)據(jù)來源中抽取的缺陷數(shù)據(jù), 其對代碼缺陷的表征能力差距較大, 因此不適合從數(shù)值(例如F1等)上進行橫向比較. 從實際應(yīng)用的角度來看, 缺陷檢測系統(tǒng)需要具備對真實工業(yè)代碼的檢測能力, 即理想的缺陷代碼數(shù)據(jù)集應(yīng)來源于NVD等工業(yè)代碼而非人工撰寫的測試用例. 但工業(yè)代碼缺陷數(shù)據(jù)的獲取難度與成本遠大于測試用例, 需要更加完善的樣本提取技術(shù)與大量不可避免的人工審查, 這可能需要相關(guān)領(lǐng)域的研究者共同努力.
從數(shù)據(jù)粒度上看, 不同的數(shù)據(jù)粒度之間難以進行統(tǒng)一評價. 當前, 缺陷代碼數(shù)據(jù)集沒有一個統(tǒng)一的數(shù)據(jù)粒度, 在文件級數(shù)據(jù)粒度過大成為共識后, 當前研究主要使用函數(shù)級數(shù)據(jù)與切片級數(shù)據(jù). 然而如第2.2.3節(jié)所述, 即使是由同一個缺陷條目構(gòu)造的函數(shù)級數(shù)據(jù)與切片級數(shù)據(jù), 因其代碼切割與分離方式的不同, 也無法進行橫向的比較. 因此, 必須明確統(tǒng)一的數(shù)據(jù)粒度, 才能保證數(shù)值比較的公平性. 函數(shù)級數(shù)據(jù)在劃分時成本極低, 且在檢測過程中不需要對被測代碼進行分析(切割為函數(shù)即可). 而切片級數(shù)據(jù)需要在構(gòu)建數(shù)據(jù)集與檢測被測代碼時均對代碼進行數(shù)據(jù)流、控制流等分析, 對資源的需求較大, 時間成本也較高. 然而, 函數(shù)級數(shù)據(jù)基于的樸素假設(shè)是: 缺陷發(fā)生在函數(shù)的范圍內(nèi). 這在大規(guī)模工業(yè)代碼中顯然是難以支持的, 特別是在模塊化的情況下, 數(shù)據(jù)的聲明與使用往往不存在于單一函數(shù)中. 因此, 代碼切片應(yīng)該是更為合理的數(shù)據(jù)粒度, 可以更加完整地表征缺陷的流程.
從標注方式上看, 不同的標注方式使得標簽的正確性存在差異. 安全相關(guān)人員的人工標注能夠保證標簽的質(zhì)量, 但其獲取成本較高. 為了解決這一問題, 許多研究者嘗試使用啟發(fā)式方法、自動化方法快速地進行標注. 然而就目前的研究情況看, 自動化與啟發(fā)式方法均難以達到理想的標注效果. 因此在現(xiàn)階段, 使用人工標注來構(gòu)造通用數(shù)據(jù)集是無法避免的.
針對傳統(tǒng)缺陷檢測方法的統(tǒng)一評測, 有研究者進行了嘗試. Zhang等人[86]提出了iTES, 其首先自動構(gòu)建了一個缺陷代碼庫, 通過控制模塊選擇測試使用的條目供被測系統(tǒng)檢測, 通過監(jiān)控模塊記錄測試結(jié)果并通過評估模塊對各項測試數(shù)據(jù)進行生成. iTES最終會對檢測系統(tǒng)的報出數(shù)目、誤報率、召回率、資源使用、分析時間和關(guān)鍵程度進行評估.
該系統(tǒng)是針對靜態(tài)與動態(tài)缺陷檢測系統(tǒng)設(shè)計的, 其只依靠源代碼即可進行檢測. 而基于深度學習的缺陷檢測系統(tǒng)在測試上涉及到不同的輸入粒度(如各類屬性圖、切片等), 需要對源代碼進行較多的預處理操作, 因此還需針對該類問題進行針對性的設(shè)計.
經(jīng)過上述分析, 對于通用的缺陷代碼數(shù)據(jù)集, 我們認為其應(yīng)該來源于真實的工業(yè)軟件代碼, 使用切片對缺陷路徑進行分離, 通過足量的人工審查進行標注, 并進行充分的多粒度預處理操作. 這些步驟都具有較高的成本, 需要該領(lǐng)域研究者的共同努力.
4.2
利用無標記數(shù)據(jù)
當前, 使用深度學習方法來進行缺陷檢測, 絕大多數(shù)都遵循著傳統(tǒng)的“l(fā)earn-from-bugs”流程, 即先構(gòu)造帶標簽的缺陷代碼數(shù)據(jù)集, 再搭建深度神經(jīng)網(wǎng)絡(luò)來挖掘并學習數(shù)據(jù)集中的特征. 因此, 神經(jīng)網(wǎng)絡(luò)的缺陷檢測能力極大程度上會受到數(shù)據(jù)集質(zhì)量的影響. 這就意味著需要大量的標注數(shù)據(jù)來訓練神經(jīng)網(wǎng)絡(luò). 然而, 有別于其他深度學習任務(wù)(例如圖像識別)可以較低成本地獲得標注數(shù)據(jù), 缺陷代碼的數(shù)據(jù)標注需要較好的代碼相關(guān)知識, 因此其必須由專業(yè)的從業(yè)人員完成, 導致其樣本獲得成本較高.
此外, 通過標注的缺陷代碼數(shù)據(jù)集訓練得到的深度學習模型, 只能針對數(shù)據(jù)集擁有的缺陷類型進行檢測, 若要對檢測缺陷類型進行擴展, 則必須增加大量的新類型標注數(shù)據(jù), 擴展能力有限.
然而, 在日漸龐大的開源代碼庫中, 有海量無標簽的代碼資源. 因此, 如何利用海量未標記數(shù)據(jù), 是一個重要的研究點.
Ahmadi等人[87]使用非學習的方法, 在無標簽的代碼中通過2次聚類, 先找到功能相近的代碼, 再從中找到類內(nèi)有差異的代碼, 通過“大部分代碼是無缺陷”的假設(shè), 將聚類中有差異的代碼標記為缺陷代碼, 即不需要額外信息即可自動完成缺陷數(shù)據(jù)的標注.
在深度學習的框架下, 同樣可以利用相似思路, 對無標簽的數(shù)據(jù)加以表示與利用.
為了應(yīng)對標注的缺陷數(shù)據(jù)較少的問題, Allamanis等人[38]提出了一種自監(jiān)督的缺陷檢測模型. 其使用一個選擇器對正確代碼進行變異, 再通過一個檢測器對變異的位置與類型進行判斷, 通過對抗的方式使得檢測器能夠具備檢測困難缺陷的能力. 由于其變異模式只有4種, 因此當檢測器預測出缺陷模式后, 即可通過逆向變異完成對該缺陷的修復. 但有限的變異模式也導致了該方法在缺陷類型上的泛化能力有限. 在針對真實項目的檢測中, 該方法報出了19個真實缺陷, 然而其誤報率高達98%, 在實用性上還有很大差距. 這同時也證實了使用人工變異構(gòu)造的缺陷數(shù)據(jù)難以匹配真實缺陷的復雜度. 該模型已開源(https://github.com/microsoft/neurips21-self-supervised-bug-detection-and-repair).
除此之外, 語言模型近年來在自然語言處理領(lǐng)域展現(xiàn)出的強大能力, 也為無監(jiān)督挖掘數(shù)據(jù)特征提供了新的思路. 當前已有一些研究者在探究代碼語言模型的構(gòu)建與應(yīng)用, 如CodeBER[71]與CuBERT[88]. 此類代碼語言模型在代碼的簡單缺陷(如變量誤用)與錯誤檢測上具有一定的效果, 但在復雜缺陷檢測任務(wù)下能力有限[88].因此, 如何構(gòu)建適用于缺陷檢測任務(wù)的代碼語言模型, 還需要進一步的探究.
當前已有較多的無監(jiān)督、半監(jiān)督方法被應(yīng)用在自然語言處理領(lǐng)域并取得了不錯的效果. 將無監(jiān)督、半監(jiān)督方法應(yīng)用在缺陷檢測領(lǐng)域, 可以極大地解決標注數(shù)據(jù)獲取成本高的問題.
4.3
明確深度學習能力邊界
代碼缺陷的類型眾多, 以CWE分類標準為例, 其將缺陷分為了10個大類, 其中的二級分類就有上百種之多, 這些不同的缺陷類型所擁有的特征和模式也是千差萬別的. 傳統(tǒng)的缺陷檢測方法會對每一個缺陷類型有針對性地設(shè)計缺陷規(guī)則, 而當前的深度學習缺陷檢測方法沒有對其類型間差異的問題進行處理, 大多簡單地將其按照二分類或多分類任務(wù)對待. 從缺陷原理上看, 其不同大類與小類在難易程度、復雜程度上存在較大的差異. 因此, 深度學習在不同缺陷類型上的效果, 即哪些缺陷類型適合使用深度學習方法, 是值得探究并明確的.
同時, 由于不同的缺陷類型在缺陷原理上的差異, 可能適合不同的深度網(wǎng)絡(luò)機構(gòu)對其進行挖掘. 因此, 對單一缺陷類型適合何種深度網(wǎng)絡(luò), 也是一個值得探究的研究點.
Yuan等人[89]對基礎(chǔ)的深度學習模型進行了初步的評估嘗試, 他們在3個項目的數(shù)據(jù)下比較了GRU、bi- GRU、DNN、LSTM這4種模型, 并對其訓練效率進行了比對. 從訓練效率上看, DNN顯著優(yōu)于其他幾種序列模型. 但是該研究只在準確度(accuracy)上進行了測試, 由于數(shù)據(jù)集中正負樣本不平衡問題較為嚴重, 該度量值并不能充分反映模型的判斷能力. 對于模型的適用性還需要更加細致、全面的評估.
當前對缺陷檢測的研究中, 從原始的完整源代碼到最后抽取出的缺陷片段與判斷結(jié)果, 中間的各個步驟都可以使用人工審查、傳統(tǒng)規(guī)則與深度學習的方法進行. 例如, 當前有探究使用深度學習來輔助SMT求解的研究工作, 雖然使用少量數(shù)據(jù)訓練來獲得精準的SMT求解能力是非常困難的, 但是利用深度學習對中間步驟進行篩選和排序, 可以提高SMT求解的效率. 同理, 在缺陷檢測的任務(wù)背景下, 應(yīng)當從應(yīng)用的角度出發(fā), 即從大規(guī)模工程的檢測角度出發(fā), 綜合地考慮與比對適合每個步驟使用的技術(shù), 而不是僅僅將缺陷檢測當作一個獨立的分類任務(wù).
4.4
大規(guī)模工程的檢測
當前, 對深度學習在源代碼缺陷檢測領(lǐng)域的研究大多停留在學術(shù)探究階段, 往往只將其當作一項獨立的分類任務(wù), 即對數(shù)據(jù)集中的條目進行分類. 事實上, 源代碼缺陷檢測作為軟件開發(fā)過程中重要的一環(huán), 具有較大的實際應(yīng)用意義. 因此, 同樣需要從工業(yè)應(yīng)用的視角對待該項研究, 從工業(yè)視角的大規(guī)模工程的檢測角度出發(fā), 其要保證流程的完整性與方法的可用性.
流程的完整性, 即從原始的全部工程代碼到抽取待測片段, 到模型生成缺陷判斷結(jié)果, 中間的各個步驟均可以使用人工審查、傳統(tǒng)規(guī)則與深度學習的方法進行. 需要綜合地考慮與對比適合每個步驟使用的技術(shù).
而方法的可用性, 則對整套流程的效率提出了要求.
當前的相關(guān)研究往往忽略了以上兩點. 例如: 使用圖神經(jīng)網(wǎng)絡(luò)對經(jīng)過預處理的代碼屬性融合圖進行分類能夠取得較好的數(shù)值效果, 但在實際的大規(guī)模工程檢測任務(wù)中, 對百萬行的工程代碼進行代碼屬性融合圖的構(gòu)造會耗費極大的計算資源與時間. 因此在后續(xù)的研究中, 如何適配大規(guī)模工程的實際檢測場景, 是一個需要重點關(guān)注的研究點.
5 總結(jié)
基于深度學習進行源代碼缺陷檢測利用了深度神經(jīng)網(wǎng)絡(luò)自動挖掘深層特征的能力, 將安全開發(fā)者從繁重的規(guī)則編寫與特征工程中解脫出來, 因此也受到越來越多的關(guān)注. 本文針對該領(lǐng)域近5年的論文發(fā)表情況進行了詳細的分析, 從數(shù)據(jù)集構(gòu)建與深度模型這兩個方面對當前的技術(shù)進行了分類與總結(jié), 并對面臨的挑戰(zhàn)與未來可能的研究重點進行了闡述.
在大量研究者的探究與實驗下, 深度學習對于缺陷檢測任務(wù)的能力已經(jīng)得到了驗證, 深度學習對挖掘代碼中語義與結(jié)構(gòu)信息的能力相對傳統(tǒng)方法存在其優(yōu)勢. 但是需要看到的是: 深度學習在缺陷檢測方向上的應(yīng)用歷史較短, 當前依然存在較多基礎(chǔ)性問題亟待解決, 在數(shù)據(jù)集與模型層面都有其改進的方向. 特別是在通用數(shù)據(jù)集的建設(shè)上, 期待能有更多的研究人員共同努力, 為該領(lǐng)域的未來發(fā)展構(gòu)建堅實的數(shù)據(jù)基礎(chǔ).
本文僅做學術(shù)分享,如有侵權(quán),請聯(lián)系刪文。


