王 凱,孔祥營,2
(1.江蘇自動化研究所,江蘇 連云港 222006;2.南京航空航天大學(xué),江蘇 南京 210016)
隨著軟件規(guī)模越發(fā)龐大,其包含的bug也越來越多,再加上軟件文檔不齊全,嚴(yán)重影響了軟件的質(zhì)量。為了提高軟件質(zhì)量,需要進(jìn)行軟件測試。軟件測試是保證軟件可靠性的重要手段,其目的是盡可能多地暴露軟件中潛在的各種錯誤。
軟件測試從是否運(yùn)行被測軟件角度分為靜態(tài)測試和動態(tài)測試。靜態(tài)測試指在不運(yùn)行被測軟件情況下用于發(fā)現(xiàn)軟件缺陷所進(jìn)行的測試,包括人工進(jìn)行的代碼檢查、文檔審查和軟件靜態(tài)分析。動態(tài)測試是指通過運(yùn)行被測軟件來檢驗(yàn)軟件的動態(tài)行為和運(yùn)行結(jié)果的正確性。軟件靜態(tài)分析是在不執(zhí)行程序的情況下對源代碼進(jìn)行控制流、數(shù)據(jù)流、接口和表達(dá)式進(jìn)行分析。軟件靜態(tài)分析是軟件測試的有效方法之一。研究表明:“bug發(fā)現(xiàn)的越晚,修正的成本就越高,即測試階段修正bug的成本是編碼階段的約4倍”[1],另一方面,“程序中的大部分錯誤往往是在一小部分模塊中發(fā)現(xiàn)的,遵循普遍適用的“二八定理”(即 80%的錯誤往往是由20%的程序模塊引起的)”[2]。在軟件生命周期的編碼階段,由于可運(yùn)行的代碼尚未產(chǎn)生,不可能進(jìn)行動態(tài)測試,但可以實(shí)施軟件靜態(tài)分析來確認(rèn)和標(biāo)識這20%程序模塊,由此可見,軟件靜態(tài)分析的作用在編碼階段尤為重要。本文對動態(tài)測試不做進(jìn)一步闡述,重點(diǎn)討論靜態(tài)測試的軟件靜態(tài)分析。
實(shí)施軟件靜態(tài)分析的最有效手段是使用靜態(tài)分析工具。C語言設(shè)計初衷主要是出于效率和靈活性兩方面考慮,靈活性帶來代碼效率的提高,但相應(yīng)帶來代碼編寫的隨意性,另外C編譯器不進(jìn)行強(qiáng)制類型檢查,也帶來代碼編寫的隱患。例如,C語言不對數(shù)組讀寫訪問越界進(jìn)行檢查。因此需要借助軟件靜態(tài)分析工具對被測程序源代碼實(shí)施軟件靜態(tài)分析來加強(qiáng)C程序缺陷檢查。本文首先對C程序包含的主要軟件缺陷進(jìn)行說明。
C程序的軟件缺陷主要有:空指針引用、懸空指針、資源泄露、函數(shù)返回值、使用未初始化變量、無限循環(huán)、死亡代碼、緩沖區(qū)溢出、野指針等。下面對這些缺陷及其原因進(jìn)行簡要描述。
1)空指針引用[3]
空指針引用會導(dǎo)致程序崩潰。空指針引用的情況包括:
①忘記對指針為NULL的情況進(jìn)行判斷;
②雖然對指針是否為NULL進(jìn)行了判斷,但沒能正確處理好。
2)緩沖區(qū)溢出[3]
緩沖區(qū)溢出是最常見的安全漏洞,包括靜態(tài)溢出和動態(tài)溢出。靜態(tài)溢出主要指數(shù)組或指針的索引為常量情況下的越界,導(dǎo)致內(nèi)存沖突,給黑客攻擊提供了可乘之機(jī),主要包括棧溢出、堆溢出、數(shù)據(jù)段溢出、BSS溢出和共享內(nèi)存溢出;動態(tài)溢出主要是數(shù)組或指針的索引在動態(tài)訪問內(nèi)存的時候越界,由于C語言固有的不安全性,數(shù)組或指針的索引不會自動去做邊界檢查,因此緩沖區(qū)溢出的情況非常普遍,所以程序員在用C語言編寫代碼時有必要檢查索引是否在有效區(qū)間內(nèi),但在復(fù)雜的函數(shù)調(diào)用和數(shù)據(jù)流傳遞中,這項工作變得異常復(fù)雜。
3)懸空指針[4]
懸空指針是由于使用了已經(jīng)釋放的內(nèi)存資源造成的。引用已釋放的指針是非常危險的,因?yàn)橹羔樀闹挡淮_定或者指針指向任意內(nèi)存的位置。為了避免懸空指針,必須在最后的引用釋放后再刪除對象。
4)資源泄漏[5]
資源泄漏是程序離開了指針的作用域后,仍沒有釋放指針占用的資源造成的。資源泄漏包括內(nèi)存泄漏、文件句柄泄漏和網(wǎng)絡(luò)套接字泄漏。
嚴(yán)重的內(nèi)存泄漏能導(dǎo)致進(jìn)程崩潰,即便是很小的內(nèi)存泄漏,在系統(tǒng)長時間運(yùn)行沒有重啟后,也會產(chǎn)生錯誤。文件句柄或者網(wǎng)絡(luò)套接字的泄漏會導(dǎo)致程序崩潰、拒絕服務(wù)攻擊或者打開其他文件或套接字失敗。操作系統(tǒng)通常會限制進(jìn)程的文件句柄和套接字個數(shù)。當(dāng)達(dá)到限制的最大值時,進(jìn)程要申請新的資源時,首先要關(guān)閉一些已打開的資源。如果進(jìn)程中存在資源泄漏,進(jìn)程自己將沒有辦法回收這些資源,除非強(qiáng)行終止該進(jìn)程。
多數(shù)情況下,這些泄漏通常發(fā)生在某個錯誤的路徑,比如說,某個異常處理的分支。這種情況下,正確的做法應(yīng)該是將程序跳轉(zhuǎn)到該函數(shù)的出口,出口處應(yīng)釋放這些資源。
5)函數(shù)返回值
函數(shù)返回值錯誤主要包括函數(shù)遺漏返回值和函數(shù)返回值可能為空或?yàn)榫植孔兞康牡刂坊驗(yàn)樨?fù)整數(shù)。函數(shù)返回值使用前,一定對其值進(jìn)行判斷。如果為空,對其非法引用將會導(dǎo)致程序崩潰;如果為負(fù)整數(shù),在作為數(shù)組索引、循環(huán)標(biāo)志變量、申請內(nèi)存大小的參數(shù)等情況下要檢查函數(shù)返回值的正確性,負(fù)整數(shù)的錯誤使用會導(dǎo)致如死循環(huán)、數(shù)組越界(內(nèi)存沖突)、變量溢出等嚴(yán)重的問題;如果是局部變量的地址,在C語言中,當(dāng)被調(diào)用函數(shù)執(zhí)行完畢,其所有的局部變量就不再有效,因此將會引起內(nèi)存沖突和不確定的行為;在返回類型為非void的函數(shù)中,值沒有被返回,比較常見的情況是,在不同的分支要返回不同的值,結(jié)果有一個分支漏了返回值。
6)使用未初始化變量
變量在使用前沒有初始化。堆棧中的變量如果沒有初始化,將沒有固定的數(shù)值。使用了沒有初始化的變量會導(dǎo)致不可預(yù)測的行為,或安全漏洞。
7)無限循環(huán)
程序中有不能終止的循環(huán),會導(dǎo)致程序掛起或者崩潰。當(dāng)然在多任務(wù)的程序中,故意設(shè)計的“死循環(huán)”除外。
8)不可達(dá)代碼
由于條件判斷總是TRUE或FALSE,導(dǎo)致一個分支內(nèi)的代碼永遠(yuǎn)執(zhí)行不到,這些永遠(yuǎn)都執(zhí)行不到的代碼稱為不可達(dá)代碼。不可達(dá)代碼一般是由邏輯錯誤引起的。在情況嚴(yán)重時,這些邏輯錯誤可能導(dǎo)致重要的代碼永遠(yuǎn)不能執(zhí)行,造成嚴(yán)重的后果。
9)野指針[6]
野指針不是NULL指針,是指向“垃圾”內(nèi)存的指針。產(chǎn)生野指針主要有兩個方面的原因:
①指針變量未被初始化。任何指針剛創(chuàng)建時不會自動成為NNLL指針,而是隨機(jī)的,它會亂指一氣。故指針創(chuàng)建時應(yīng)當(dāng)初始化,要么將指針設(shè)置為NULL,要么讓它指向合法的內(nèi)存。
②指針p被free或delete后,沒有設(shè)置為NULL。這樣讓人誤以為p是合法的指針,其實(shí)free或delete僅把p所指向的內(nèi)存釋放掉,但并沒把指針p本身釋放(仍指向原來的地址)。通常用 if(p!=NULL)進(jìn)行防錯處理,但是很遺憾,此if語句根本起不到防錯的作用,因?yàn)榧词筽不是NULL,它也不指向合法的內(nèi)存塊。
另外不要將指針或引用指向棧內(nèi)存,因?yàn)闂?nèi)存在函數(shù)結(jié)束時會被釋放。
針對C程序的軟件缺陷,人們設(shè)計了很多軟件靜態(tài)分析工具。目前問世的靜態(tài)分析工具能否有效發(fā)現(xiàn)這些軟件缺陷,需要我們對軟件靜態(tài)分析工具作進(jìn)一步研究。
軟件靜態(tài)分析工具不僅可以查找源代碼中缺陷,也可以用來發(fā)現(xiàn)與大規(guī)模代碼庫交互過程中產(chǎn)生的缺陷。構(gòu)建靜態(tài)分析工具采用一定的算法,即靜態(tài)分析技術(shù)[7-9],主要包括符號執(zhí)行、定理證明、類型推導(dǎo)、基于規(guī)則的檢查、模型檢測、分析工具中使用的規(guī)約、詞法分析、語法分析、污點(diǎn)傳播、布爾可滿足性、整數(shù)范圍分析、基于調(diào)用圖/控制流圖分析、基于程序注釋和增式分析等。其工作原理是通過算法檢查代碼的錯誤,并標(biāo)明問題區(qū)域,以便編程人員做更詳細(xì)的檢查,這種算法方式無需使用測試用例,算法本身決定了靜態(tài)分析工具發(fā)現(xiàn)錯誤的效率。
靜態(tài)分析技術(shù)和靜態(tài)分析工具二者是相輔相成,互相促進(jìn)。隨著人們對軟件質(zhì)量要求越來越高,靜態(tài)分析技術(shù)不斷出現(xiàn),同時伴隨著軟件靜態(tài)分析工具的問世。通過對軟件靜態(tài)分析工具的應(yīng)用研究,軟件靜態(tài)分析工具存在的缺陷與不足逐漸暴露出來,又促使人們不斷改進(jìn)現(xiàn)有的和提出新的靜態(tài)分析技術(shù)。
目前可供選擇的靜態(tài)分析工具很多,主要包括Klocwork K7[8]、FlawFinder[9]、BOON[9]、MOPS[10]、Coverity Prevent[10]、Splint[11]、PolySpace Verifier[13-14]等。合適的靜態(tài)分析工具將大大提高靜態(tài)分析效率,因此如何選擇靜態(tài)分析工具非常關(guān)鍵。選擇靜態(tài)分析工具需要權(quán)衡的因素主要包括:靜態(tài)分析工具的誤報率和bug率[14-15]、支持的編程語言、性價比、易于安裝和使用、使用靜態(tài)分析工具的時機(jī)和頻率等。以PolySpace Verifier、Splint、Klocwork K7、Coverity Prevent為代表的靜態(tài)分析工具正將工具的作用向軟件生命周期的早期階段推進(jìn),促進(jìn)了靜態(tài)分析的發(fā)展。下面對主流靜態(tài)分析工具進(jìn)行簡要介紹:
1)PolySpace Verifier
PolySpace Verifier是由PolySpace公司開發(fā)的商業(yè)靜態(tài)分析工具,用于發(fā)現(xiàn)運(yùn)行時 bug。已經(jīng)在嵌入式系統(tǒng)相關(guān)領(lǐng)域得到廣泛使用,如自動化、航空航天、國防部和鐵路交通等領(lǐng)域。當(dāng)進(jìn)行靜態(tài)分析時,PolySpace能識別算術(shù)異常、數(shù)組索引越界、未初始化局部變量、指針無效解除引用、無效類型轉(zhuǎn)換、緩沖區(qū)溢出、未保護(hù)共享數(shù)據(jù)以及不可達(dá)代碼等bug。
PolySpace Verifier將以橙色標(biāo)識大量代碼表示代碼可能包含一個缺陷,綠色標(biāo)識的代碼表示代碼沒有缺陷,紅色標(biāo)識代碼表示的確是缺陷,而灰色標(biāo)識代碼表示死亡代碼(即無法執(zhí)行的代碼)。
2)Splint
Splint是開源的靜態(tài)分析工具,用于檢測用標(biāo)準(zhǔn)C實(shí)現(xiàn)的軟件缺陷,通過在源代碼中添加關(guān)于函數(shù)、變量、參數(shù)和類型的格式化注釋,實(shí)現(xiàn)了未使用的聲明、類型不一致性、使用未定義變量、內(nèi)存泄漏、不可達(dá)代碼、函數(shù)返回值、無限循環(huán)、危險的別名等軟件缺陷。
3)Klocwork K7
Klocwork K7是商業(yè)靜態(tài)分析工具,目前最新版本為Klocwork Insight。支持C/C++/Java,也是宣稱能處理 C/C++/Java三種語言寫的復(fù)合應(yīng)用程序的唯一提供商。Klocwork K7能識別內(nèi)存泄露、緩沖區(qū)溢出、使用未初始化變量、函數(shù)返回值等軟件缺陷。
4)Coverity Prevent
Coverity Prevent是一款商業(yè)靜態(tài)分析工具,支持C/C++/Java。Coverity支持整個系統(tǒng)的增式分析,在上次分析的基礎(chǔ)上僅僅有部分代碼被修改。分析的結(jié)果被保存并在接下來的分析中重用。這樣的增式分析比分析整個系統(tǒng)所花時間明顯要少很多。Coverity Prevent能識別死鎖、資源泄露、空指針引用、懸空指針、不可達(dá)代碼、未初始化變量、函數(shù)返回值、緩沖區(qū)溢出等軟件缺陷。
通過 PolySpace Verifier、Splint、Klocwork K7和Coverity Prevent對應(yīng)用程序靜態(tài)分析,結(jié)果見表1。
由以上分析結(jié)果知道:盡管靜態(tài)分析工具在揭露軟件缺陷方面發(fā)揮著重要作用,但靜態(tài)分析工具并不是完美的,PolySpace Verifier、Splint、Klocwork K7和 Coverity Prevent采用算法的片面性決定了它們都只能發(fā)現(xiàn)部分缺陷。為了保證軟件測試的質(zhì)量和有效性,我們認(rèn)為軟件靜態(tài)分析工具在查找軟件缺陷方面是行之有效的,但也要認(rèn)識到軟件靜態(tài)分析工具存在的不足之處。
靜態(tài)分析工具優(yōu)點(diǎn)包括[15]:
①開發(fā)早期發(fā)現(xiàn)軟件錯誤,易于修改,降低開發(fā)成本;
②防止錯誤在系統(tǒng)中擴(kuò)散進(jìn)而影響后期開發(fā)的代碼行為;
③有助于開發(fā)人員發(fā)現(xiàn)個人編碼風(fēng)格的缺點(diǎn)。
靜態(tài)分析工具缺點(diǎn)包括[15]:
①存在誤報和漏報的可能性,降低靜態(tài)分析有效性;
②輸出結(jié)果仍然需要人工確認(rèn),增加開發(fā)成本;
③采用算法的片面性,使得靜態(tài)分析不能解決所有安全問題。
表1 主流靜態(tài)分析工具比較
軟件靜態(tài)分析工具可以在軟件生命周期的編碼階段實(shí)施測試,有助于降低軟件成本和開發(fā)時間,隨著軟件靜態(tài)分析工具功能越來越強(qiáng)大,逐漸受到開發(fā)人員和測試人員重視和喜愛。但靜態(tài)分析工具存在較高漏報率和誤報率,降低了測試的有效性,另外靜態(tài)分析工具無法暴露一些運(yùn)行時錯誤,因此僅采用靜態(tài)分析還不夠,需要在軟件生命周期的晚期階段進(jìn)一步實(shí)施動態(tài)分析,可以說靜態(tài)分析是對動態(tài)分析的有效補(bǔ)充,使軟件測試更加高效。針對軟件靜態(tài)分析工具優(yōu)勢與不足并存及種類繁多的現(xiàn)狀,要選擇適合自己的工具,需要對影響軟件靜態(tài)分析工具選擇的諸多因素綜合權(quán)衡。
[1]huior.我看“靜態(tài)測試”[EB/OL].http://www.51testing.C om/?10851/action_viewspace_itemid_72777.html.2008-0 1-16.
[2]于波,姜艷.同行評審[EB/OL].http://www.uml.org.cn/rjzl/200902024.asp.2009-02-02.
[3]余建軍,韓雙霞,黃云龍.軟件安全性的靜態(tài)分析[J].計算機(jī)工程與設(shè)計,2006,27(8):1411-1414.
[4]ruanruan111.內(nèi)存出錯,內(nèi)存泄露,數(shù)據(jù)越界,懸空指針,錯誤分配[EB10L].
[5]肖慶,張威,宮云戰(zhàn),等.內(nèi)存泄漏的一種靜態(tài)分析方法[J].裝甲兵工程學(xué)院學(xué)報,2004,18(2):23-26.
[6]xserver.杜絕野指針[EB/OL].http://www.javaeye.com/wiki/topic/625904.2010-03-27.
[7]楊宇,張健.程序靜態(tài)分析技術(shù)與工具[J].計算機(jī)科學(xué)2004,31(2):171-174.
[8]夏一民,羅軍,張民選.基于靜態(tài)分析的安全漏洞檢測技術(shù)研究[J].計算機(jī)科學(xué),2006,33(10):279-282.
[9]shiliyanfir.klocwork介紹[EB/OL].http://www.docin.com/p-8466186.html.
[10]Lauri Kolmonen.Securing Network Software using Static Analysis[J].TKK T-110.5290 Seminar on Network Security 2007-10-11/12.
[11]jack2000ran.軟件錯誤及安全性檢測工具.Coverity Prevent[EB/OL].2009-02-20.
[12]sciyon_2010.Splint Manual[EB/OL].http://wenku.baidu.com/view/f2a7c50c6c85ec3a87c2c59d.html.2011-01-24.
[13]Par Emanuelsson and Ulf Nilsson.A Comparative Study of Industrial Static Analysis Tools[J.Electronic Notes in Theoretical Computer Science 217 (2008)5–21.
[14]DING Zhigang,WANG Hongcheng,LING Lianghe.Practical Strategies to Improve Test Efficiency[J].TSINGHUA SCIENCE AND TECHNOLOGY,2007,12(S1):250-254.
[15]Paul E.Black,Michael Kass,Michael Koo.Source Code Security Analysis Tool Functional Specification Version 1.0[J.NIST SP 500-268 May 2007:1-14.
[16]鄭人杰.計算機(jī)軟件測試技術(shù)[M].北京:清華大學(xué)出版社,1997..