一種代碼靜態(tài)檢測方法
【技術領域】
[0001] 一種代碼靜態(tài)檢測方法,應用于源代碼中的數(shù)組越界,除零錯誤和空指針引用,設 及編譯器理論,程序內(nèi)存模型,SMT約束求解,形式化方法,一階謂詞邏輯等領域,具體設及 一種基于LLVMIR的符號執(zhí)行算法一一利用給予程序變量符號初值技術領域。
【背景技術】
[0002] 隨著計算機技術的發(fā)展,軟件系統(tǒng)已經(jīng)被運用到我們生活的方方面面,依賴互聯(lián) 網(wǎng)我們足不出戶就可W網(wǎng)上購物(淘寶/京東)、接受遠程教育(coursera/網(wǎng)易公開課)、 與朋友聊天(QQ/微信)、甚至網(wǎng)上看病等。
[000引 2014年9月19日,阿里己己在美國紐交所正式進行IP0,其市值超過化cebook成 為僅次于google的全球第二大互聯(lián)網(wǎng)公司。該也標志著我國口業(yè)逐漸走向國際市場。近 年來我國IT業(yè)發(fā)展迅速,2013年我國軟件業(yè)實現(xiàn)業(yè)務收入總量規(guī)模達到3. 1萬億元,同比 增長24. 6%,比同期電子信息制造業(yè)增速高13. 6%。與此同時我國軟件應用水平和產(chǎn)業(yè)創(chuàng) 新能力也得到逐步提升。2013年軟件類產(chǎn)品登記數(shù)總數(shù)達39821件,相比同期增長11. 5%, 軟件著作權登記數(shù)量達164349件,相比同期增長18. 04%,整個行業(yè)總體投入研發(fā)經(jīng)費同 比增長6. 3%。
[0004] 軟件技術的發(fā)展極大的方便了我們的生活,提高了我們的工作效率。軟件業(yè)蓬勃 發(fā)展,未來會有更多人員投入到軟件開發(fā)中,軟件開發(fā)對從業(yè)人員的口檻也會越來越低。新 華(大連)軟件和信息技術服務業(yè)發(fā)展指數(shù)報告指出2014年中國信息技術服務業(yè)和軟件 開發(fā)從業(yè)人員高達470萬,是2001年數(shù)據(jù)的15倍。
[0005] 但是另一方面由于軟件的廣泛使用使得一旦軟件出現(xiàn)崩潰或者被惡意攻擊便會 帶來極為嚴重的后果,所W對于軟件質(zhì)量的要求將越來越高。該種從業(yè)口檻的降低和對軟 件質(zhì)量要求的增高無疑形成了一對激烈的矛盾。該種矛盾的直接后果就是對軟件測試、源 代碼分析等技術提出了更高的要求。
[0006] 近些年來,隨著民用航空事業(yè)的蓬勃發(fā)展和部隊飛行訓練任務的逐步加重,國內(nèi) 飛機的數(shù)目與日俱增,空域內(nèi)航線日益密集,飛行器流量日益加大,又由于飛機只能在平流 層平穩(wěn)飛行,使得飛機可飛行空間是一個相對有限的空間,該就造成了空域變得越來越擁 擠,擁擠意味著沖突。無論是航線設定過密,飛機本身故障或是風力等環(huán)境因素,都可能造 成飛機發(fā)生碰撞沖突。由于飛機運輸?shù)奶厥庑裕坏╋w機在空中發(fā)生沖突,就很難保證乘客 人身及財產(chǎn)安全。同時,如不能有效地疏通該種擁擠,也會降低空域資源的利用率,極大地 阻礙國家航空事業(yè)的發(fā)展。因此,能夠提前預知沖突的發(fā)生,并及早地采取有效的防范措施 就顯得尤為重要。
[0007] 符號執(zhí)行能夠有效的給復雜的程序生成高覆蓋率的測試用例和發(fā)現(xiàn)深層次的漏 洞,最近幾年受到了極大的關注。符號執(zhí)行的核屯、思想在1976年被引入,但最近幾年才被 有效的運用于實際工程中。主要原因在于約束求解器等技術的發(fā)展符號執(zhí)行的一個關鍵目 標是在給定的時間內(nèi)探索盡量多不同的程序路徑,對于每一條路徑(1)生成一組具體的輸 入使得程序在實際運行時能夠執(zhí)行該條路徑。(2)在該路徑上檢測各種常見的錯誤如斷言 失敗,未捕獲異常,內(nèi)存泄露等。能生成具體的測試用例是符號執(zhí)行的一個強大能力。從軟 件測試的角度,它能產(chǎn)生高覆蓋率的的輸入,從探測bug的角度,它能給開發(fā)者提供一個能 觸發(fā)bug的輸入,W便于幫助開發(fā)者分析漏洞是如何發(fā)生的。在漏洞的檢測中,從某種程度 上來說符號執(zhí)行相比于傳統(tǒng)的測試執(zhí)行技術如Valgrind和化rify等更加強大。因為該些 傳統(tǒng)工具依賴于實際執(zhí)行程序所輸入的參數(shù),如果該些參數(shù)不能夠觸發(fā)漏洞,則該些工具 是無法發(fā)現(xiàn)該些漏洞的。另外符號執(zhí)行不但能探測到傳統(tǒng)測試工具能檢查到的漏洞,如內(nèi) 存泄露等,更能檢查到一些程序高層次的性質(zhì),比如復雜的程序斷言邏輯。
【發(fā)明內(nèi)容】
[000引本發(fā)明針對現(xiàn)有技術的不足之處提供了一種代碼靜態(tài)檢測方法,解決現(xiàn)在技術中 的靜態(tài)檢測方法支持語言單一、不能較好的處理指針和別名問題,探測潛在的程序漏洞等 問題。
[0009] 為了實現(xiàn)上述目的,本發(fā)明采用的技術方案為:
[0010] 一種代碼靜態(tài)檢測方法,其特征在于,如下步驟:
[0011] (1)獲取源代碼,并將源代碼進行預處理轉換為LLVM匯編程序;
[0012] (2)將轉換得到的LLVM匯編程序,運用符號執(zhí)行算法模擬解釋執(zhí)行LLVM匯編程 序,并記錄各個變量在不同路徑上的符號值和各條路徑的約束條件;
[0013] (3)根據(jù)記錄各個變量在不同路徑上的符號值和各條路徑的約束條件,調(diào)用SMT 求解器Z3檢查變量的符號值是否滿足路徑約束和漏洞約束,判斷程序是否存在潛在漏洞。
[0014] 進一步,所述步驟(1)中,將源代碼進行預處理轉換為LLVM匯編程序的具體步驟 如下:
[0015] (11)調(diào)用編譯器clang檢查源代碼是否存在語法錯誤,如有錯誤,修改語法錯誤 直到編譯器不報錯,W得到語法正確的源代碼;
[0016] (12)將得到的語法正確的源代碼利用編譯器Clang轉化為LLVM匯編程序。
[0017] 進一步,所述步驟(2)中,模擬解釋執(zhí)行LLVM匯編程序的具體步驟如下:
[001引 (21)調(diào)用LLVM基礎庫,將LLVM匯編程序輸入LLVM基礎庫并轉化為文本形式程序 的LLVM匯編程序CFG,LLVM基礎庫返回的CFG中包含的具體對象有全局變量,全局變量對 應全局變量對象g,每一個函數(shù)LLVM匯編程序?qū)瘮?shù)對象F,每個函數(shù)對象F包含函數(shù)參 數(shù)P和若干代碼塊b,而每個代碼塊又包含多個指令instruction;
[0019] (22)對于被分析函數(shù)對象,為其引用到全局變量對象、函數(shù)參數(shù)和內(nèi)存對象,內(nèi)存 對象是一個四元組,其結構為:
[0020] SValue=(化XvalXptrXdata)G(llvm: :Type*XStringXSValue*X(intXS Value*)),
[002U其中SValue是內(nèi)存對象,用于描述各變量的符號狀態(tài),tp、val、ptr和data分別 是,化代表數(shù)據(jù)的類型,val代表符號值,表示一個字符串的類型為String,ptr代表符號 指針值,data是一個數(shù)字到符號值指針的映射,用于記錄數(shù)組類型和結構體,llvm::Type*, String,SValue*,(intXSValue*)分別是對應分量tp、val、ptr、data的數(shù)據(jù)類型, LIvm: :Type是LLVM基礎庫中的一個描述LLVM匯編程序類型的對象,LLVM: :Type*代表該 類型對象的指針,string是C++標準庫中的字符串對象類型,SValue是內(nèi)存對象,也稱為符 號對象,int代表C++基礎類型中的整數(shù)類型;
[0022] 系統(tǒng)的初始狀態(tài)為PT、PTS,首先初始化一個長度為1的根路徑PT,將該路徑存放 到可執(zhí)行路徑PTS中,解釋執(zhí)行的過程類似于一個有限狀態(tài)機的狀態(tài)遷移,解釋器的狀態(tài) WPT和PTS進行描述,每解釋執(zhí)行一條指令狀態(tài)就發(fā)生遷移,將初始化好的第一條路徑放 入PTS中;該些初始化的過程可W表示為如下指令語義模式操作公式:
[0023]
[0024] 其中,PT代表當前正在探索的可行路徑,PTS代表目前發(fā)現(xiàn)的所有未探測的可行 路徑集合,B代表LLVM匯編程序CFG中的一個代碼塊,PT.cuitB代表當前路徑正在探索的 代碼塊,F(xiàn)表示當前正在被分析的函數(shù),F(xiàn).firs巧lock代表函數(shù)的第一個代碼塊,PT.vars 代表路徑上所有的變量,它是一個映射,PT.vars[key=value]代表的是將鍵值對化ey, value)存入映射vars當中,其中key代表映射的索引,value代表映射的key索引所對 應的值,k巧和value可W被替換為各種值,公式中PT.vars[g=SValue(g.typeO),P =SValue(p.type0)]是PT.vars[g=SValue(g.type())]和PT.vars[p=SValue(p. type0)]的縮寫形式,代表將鍵值對(g,SValue(g.type〇))和(p,SValue(p.type))存入 PT.vars中,此處的g和p作為key,SValue(g.type0)和SValue(p.type)則作為值value 部分,SValue(type)是符號對象的構造函數(shù),它有一個參數(shù)type表明符號對象,即表示數(shù) 據(jù)的類型,g代表全局變量,g.type0表示全局變量的類型,P代表函數(shù)F的參數(shù),P.type0 表示參數(shù)的類型,SValue(p.typeO)代表根據(jù)參數(shù)P的類型構造內(nèi)存對象,SValue(g. type0)代表根據(jù)全局變量g的類型構造內(nèi)存對象,PT.it代表當前路徑所執(zhí)行到的指令, PT.currB.firstinstruction代表當前路徑當前塊的第一條指令,PTS.add(PT)將一條路 徑PT添加到可行路徑集合PTS中,init代表該是一個初始化動作,在任何具體指令分析之 前應該進行該操作,它不對應任何指令;
[0025] (23)判斷PTS是否為空,如果PTS不為空,則彈出其中一條路徑存放到PT中,并執(zhí) 行步驟(24);若PTS為空,則模擬執(zhí)行結束;
[0026] (24)若PT路徑中的當前執(zhí)行位置PT.it所指指令為非跳轉指令,則執(zhí)行步驟 (25),否則執(zhí)行(26);
[0027] (25)根據(jù)不同的非跳轉指令類型,執(zhí)行不同的指令語義模式操作公式;
[002引 (26)根據(jù)不同的跳轉指令類型,執(zhí)行不同的指令語義模式操作公式;
[0029] (27)判斷步驟(25)或步驟(26)直到發(fā)現(xiàn)致命性錯誤或者PTS為空,否則PT.it++ 繼續(xù)執(zhí)行步驟(23)至步驟(26)。
[0030] 進一步,所述步驟(22)中,指令語義模式的結構如下公式所示:
[0031]
[0032] 其中〈currentstate〉代表系統(tǒng)的當前狀態(tài),<endstate〉代表語句執(zhí)行后程序 的狀態(tài),computation是狀態(tài)遷移計算過程的一些細節(jié)展示,stmt代表被執(zhí)行的語句包含 的所有指令,具體指令如下所示:
[0033] (11)ret[voidI<typeXvalue>];
[0034] (I2)briKcond〉,1油eKiftrue〉,labeKiffalse〉;
[0035] (I3)br1油eKdest〉;
[0036] (14) <result> =[add|sub|mul|udiv|sdiv|urem|srem] <tyXopl>, <op2>;
[0037] (15)<result> =icmp<cond><tyXopl>, <op2> ;
[0038] (16)<result> =alloca<type>[, <ty><NumElements>];
[0039] (17)〈result〉 =load<ty〉*<pointer〉;
[0040] (18)store<tyXvalue>, <ty>*<pointer> ;
[0041] (19)<result> =getelementptrinbounds<pty〉*<ptrval〉{,<tyXidx〉} * ;
[0042] (110)<result> =invoke<ptrtofunctiontyXfunctionptrval>?function args>;
[0043] II代表函數(shù)返回指令,它分為有參數(shù)值和無參數(shù)值返回,無參數(shù)值retvoid,有參 數(shù)值ret<typeXvalue〉,其中〈value〉代表具體的返回值,〈type〉代表返回值的類型,ret 表示該是一條函數(shù)返回;
[0044] 12代表有條件跳轉指令,其中<cond>代表跳轉的條件,il代表布爾類型, <iftrue>代表條件為真時跳轉到的目標代碼塊,<ifTalse>代表條件為假時跳轉到的目標 代碼塊,1油el代表是一個標簽類型,br表示該是一條跳轉指令;
[0045] 13代表無條件跳轉指令,<dest>代表無條件跳轉到的目標代碼塊,1油el代表是 一個標簽類型,br表示該是一條跳轉指令;
[0046] 14代表算術運算指令,[