靜態(tài)分析工具正越來越多地集成到軟件開發(fā)過程中。在過程中保存來自編譯器的數(shù)據(jù)、更改歷史記錄和錯(cuò)誤信息,而不是作為后置代碼步驟,可以提高靜態(tài)分析的效率。
高級靜態(tài)分析工具在嵌入式系統(tǒng)開發(fā)中變得越來越重要。遠(yuǎn)遠(yuǎn)超出實(shí)際上是編碼風(fēng)格檢查器的舊靜態(tài)分析工具,新工具靜態(tài)分析源程序的控制和數(shù)據(jù)流,從而檢測錯(cuò)誤和漏洞,例如潛在的緩沖區(qū)溢出、未初始化變量的使用、通過空指針訪問,以及對安全攻擊(SQL 注入、跨站點(diǎn)腳本等)的敏感性。
然而,這些先進(jìn)的工具提出了幾個(gè)問題。首先,工具需要了解被分析程序的語義——也就是說,它們必須編譯程序——以執(zhí)行所需的控制和數(shù)據(jù)流分析。為此,它們必須緊密集成到構(gòu)建環(huán)境中,以便在編譯時(shí)識(shí)別和使用所有可能需要的包含文件或其他規(guī)范模塊。其次,這些高級工具產(chǎn)生的輸出量可能令人望而生畏,每條診斷信息都需要仔細(xì)審查,以確定它是否反映了一個(gè)真正的問題,如果是,如何解決它。
將靜態(tài)分析工具與軟件開發(fā)工具鏈更緊密地集成可以緩解這兩個(gè)挑戰(zhàn)。在第一種情況下,將靜態(tài)分析工具與編譯器緊密集成在很大程度上消除了構(gòu)建環(huán)境問題,并使用戶界面變得簡單和熟悉。在第二種情況下,可以通過將所有輸出存儲(chǔ)在歷史數(shù)據(jù)庫中來管理大量輸出,從而允許程序員專注于已知良好版本和源當(dāng)前狀態(tài)之間的增量,而不是在每一步處理所有消息。
與編譯器集成
超越簡單語法檢查的靜態(tài)分析工具通常需要編譯器前端的大部分功能,以便它可以根據(jù)程序的語義進(jìn)行分析。這是因?yàn)橄嗤木浞ㄐ问酵ǔ?梢愿鶕?jù)其成分的含義有不同的解釋。例如,Ada 中的表達(dá)式 F(N) 可能(除其他外)是數(shù)組引用、函數(shù)調(diào)用或類型轉(zhuǎn)換。
訪問程序的底層語義允許該工具跟隨程序中出現(xiàn)的每個(gè)名稱回到引入該名稱的聲明,即使存在重載、通用模板或重命名也是如此。該工具將知道每個(gè)對象和每個(gè)表達(dá)式的類型,并將識(shí)別任何隱式運(yùn)行時(shí)檢查發(fā)生的位置。這些隱式運(yùn)行時(shí)檢查可能包括對取消引用空指針的檢查和對數(shù)組邊界之外的索引的檢查。即使沒有隱式運(yùn)行時(shí)檢查的語言也可以將某些運(yùn)行時(shí)操作定義為具有未指定的語義,例如整數(shù)算術(shù)溢出或超出范圍的數(shù)組索引。靜態(tài)分析工具需要知道語言語義何時(shí)允許這種未指定(因此不可預(yù)測)的行為。
由于需要包含編譯器前端的強(qiáng)大功能,許多靜態(tài)分析工具都建立在感興趣的語言的現(xiàn)有編譯器技術(shù)之上。不幸的是,該工具的構(gòu)建者選擇的編譯器技術(shù)可能與該工具的客戶使用的編譯器無關(guān)。發(fā)生這種情況時(shí),靜態(tài)分析工具可能無法處理客戶編寫的代碼。
例如,如果客戶程序使用編譯器特定的功能(例如中斷處理或特殊的內(nèi)存映射工具),則無法保證靜態(tài)分析工具的底層完全或以同樣的方式支持它們。編譯器前端技術(shù)。即使對于可移植代碼,客戶的編譯器和靜態(tài)分析工具的底層技術(shù)也可能存在不同的錯(cuò)誤或?qū)φZ言規(guī)則的細(xì)微不同的解釋。即使解釋匹配,編譯程序的命令(控制源代碼搜索路徑、預(yù)處理器支持和其他功能的命令行開關(guān))也可能有很大不同。因此,復(fù)雜程序的構(gòu)建過程可能難以轉(zhuǎn)換為對程序執(zhí)行靜態(tài)分析的生成過程。
為了解決這些問題,明確的解決方案是將高級靜態(tài)分析引擎與客戶使用的相同編譯器技術(shù)集成。因此,靜態(tài)分析引擎必須在某種程度上獨(dú)立于任何特定編譯器技術(shù)使用的中間表示,以便該工具可以輕松地適應(yīng)支持多個(gè)編譯器前端。
一種方法是讓靜態(tài)分析引擎擁有自己的中間表示,專門設(shè)計(jì)用于支持工具執(zhí)行的高級分析。適應(yīng)支持新的編譯器前端需要編寫一個(gè)轉(zhuǎn)換模塊,將編譯器的中間表示(前端的輸出)轉(zhuǎn)換為靜態(tài)分析引擎使用的程序表示。翻譯模塊將結(jié)果輸出到文件中供以后使用。中間語言翻譯器既可以鏈接到編譯器前端,也可以作為獨(dú)立程序運(yùn)行,讀取編譯器的中間表示,轉(zhuǎn)換它,然后寫出分析引擎的中間表示。這個(gè)過程如圖 1 所示。
圖 1:中間語言翻譯器讀取編譯器的中間表示,對其進(jìn)行轉(zhuǎn)換,然后寫出靜態(tài)分析引擎的中間表示。
當(dāng)采用這種集成方法時(shí),靜態(tài)分析只是構(gòu)建過程的另一部分,可以在編譯期間執(zhí)行,或者為了利用整個(gè)程序分析,在鏈接步驟期間執(zhí)行。對用戶的一個(gè)關(guān)鍵優(yōu)勢是調(diào)用靜態(tài)分析工具只涉及向編譯器和/或鏈接器提供額外的命令行開關(guān)。無需為該工具創(chuàng)建專門的構(gòu)建腳本或維護(hù)兩組源(一組與編譯器一起使用,另一組與靜態(tài)分析工具一起使用)。
與開發(fā)環(huán)境集成
由于軟件開發(fā)通常通過 Eclipse 等圖形集成開發(fā)環(huán)境 (IDE) 進(jìn)行,因此將靜態(tài)分析工具和編譯器集成到 IDE 中是很自然的。然后,程序員將立即熟悉該工具的整體界面,從而減少學(xué)習(xí)曲線并增加定期使用該工具的可能性。
靜態(tài)分析工具生成的消息必須像編譯器生成的錯(cuò)誤或警告消息一樣處理,并由用戶以相同的方式管理和查看。鑒于正在使用多個(gè) IDE,每個(gè) IDE 都有自己的消息格式,靜態(tài)分析工具將需要表示其消息,以便可以輕松地將它們轉(zhuǎn)換為 IDE 期望的任何格式。
消息表示的自然選擇是 XML,因?yàn)樗褂脴?biāo)記的、自描述的方法來捕獲消息特征。使用 XML 的一個(gè)附帶好處是它有助于簡化應(yīng)用程序的國際化過程,從而可以以客戶喜歡的自然語言顯示消息。
與歷史數(shù)據(jù)庫集成
一旦高級靜態(tài)分析工具與編譯器和 IDE 集成,下一個(gè)問題就是處理此類工具可能提供的大量消息。因?yàn)楦呒夓o態(tài)分析工具正在尋找可能的運(yùn)行時(shí)邏輯錯(cuò)誤和安全漏洞,所以它們必須模擬運(yùn)行時(shí)程序的執(zhí)行(識(shí)別一組潛在的執(zhí)行狀態(tài))并確定在什么條件下可能會(huì)達(dá)到不期望的狀態(tài)。不幸的是,這很少是簡單的“是”或“否”。有許多灰色陰影,其中脆弱性程度取決于工具可能未知或超出其分析能力的因素。
這個(gè)問題有時(shí)用健全性與精確性來表述。如果一個(gè)搜索有問題結(jié)構(gòu)的工具能夠識(shí)別出它正在尋找的所有問題(沒有誤報(bào)),那么它就被認(rèn)為是可靠的。但穩(wěn)健性通常是以犧牲精度為代價(jià)的。該工具可能會(huì)生成大量誤報(bào),這些誤報(bào)是用于識(shí)別并非真正問題的警告或錯(cuò)誤。考慮這個(gè)使用類 C 語法的簡單示例:
int k, m, n;
... // Complicated code that assigns a positive value to m
... // and that does not assign to n
if (m《0){
k=n;
...
}
工具可能無法推斷 if 語句上的 m《0 條件為假,因此可能會(huì)警告 if 語句的主體引用未初始化的變量 (n)。實(shí)際問題恰恰相反:if 語句的主體是永遠(yuǎn)不會(huì)執(zhí)行的代碼,有時(shí)稱為死代碼或無法訪問的代碼。
工具開發(fā)人員必須決定是選擇健全性(確保沒有未被檢測到的實(shí)際違規(guī))還是精度(確保所有報(bào)告的違規(guī)都是真正的錯(cuò)誤)。當(dāng)一個(gè)工具用于安全關(guān)鍵或高安全性系統(tǒng)時(shí),天平就會(huì)傾斜。使用此類工具的開發(fā)人員必須有信心檢測到所有違規(guī)行為。但這引發(fā)了前面提到的關(guān)于如何處理可能產(chǎn)生的大量誤報(bào)的問題。當(dāng)該工具應(yīng)用于遺留軟件(在應(yīng)用靜態(tài)分析工具之前開發(fā)的代碼)時(shí),這個(gè)問題尤其明顯。對于大型應(yīng)用程序,用戶需要查看的消息數(shù)量可能令人望而生畏。
將高級靜態(tài)分析工具與歷史數(shù)據(jù)庫集成,可以有效地使用該工具,最大限度地減少誤報(bào)導(dǎo)致的問題,即使對于在使用該工具之前開發(fā)的復(fù)雜應(yīng)用程序也是如此。關(guān)鍵概念是基線的概念以及該工具突出顯示相對于此類基線的增量的能力。通過在歷史數(shù)據(jù)庫中記錄每個(gè)工具運(yùn)行的所有結(jié)果,該工具可以識(shí)別任何兩次運(yùn)行之間的增量(更改)。
數(shù)據(jù)變得更有用
為了使分析運(yùn)行之間的比較有效地進(jìn)行,消息必須是唯一可識(shí)別的,而不涉及特定的行號(hào),可以從源代碼的一個(gè)版本切換到另一個(gè)版本而無需任何重大更改。識(shí)別沒有行號(hào)的消息的一種方法是記錄消息的文本(或相應(yīng)的 XML),以及它出現(xiàn)的函數(shù)的名稱,如果消息的文本與同一功能中的一些先前消息。
假設(shè)消息使用這個(gè)與行號(hào)無關(guān)的唯一標(biāo)識(shí)符作為關(guān)鍵字存儲(chǔ)在數(shù)據(jù)庫中,那么該工具可以輕松識(shí)別給定消息是新消息還是先前生成的消息。這使歷史數(shù)據(jù)庫的整體大小保持可管理。該工具不需要為工具的所有調(diào)用重復(fù)存儲(chǔ)所有消息的文本,而只需要存儲(chǔ)給定消息的文本一次,以及該消息出現(xiàn)的工具調(diào)用范圍的指示(第一次運(yùn)行生成消息的位置,以及第一次沒有出現(xiàn)的運(yùn)行)。
該歷史數(shù)據(jù)庫使用戶界面可以直接顯示或突出顯示自指定基線以來的新消息。這使得該工具即使在具有大量遺留代碼的大型應(yīng)用程序上也能有效使用。應(yīng)用程序的已知良好版本可以通過分析工具作為基線運(yùn)行??梢苑治鰬?yīng)用程序的當(dāng)前開發(fā)版本,并將分析此已知良好版本的結(jié)果作為基線。那些在開發(fā)版本上工作的人可以專注于與他們自已知良好版本以來所做的更改相關(guān)的任何消息,而不必費(fèi)力地處理與遺留代碼相關(guān)的消息。最終,可以致力于處理這些積壓的消息,
與歷史數(shù)據(jù)庫集成的另一個(gè)好處是能夠從查看分析結(jié)果的用戶那里收集評論。在某些情況下,可能需要對特定消息進(jìn)行大量調(diào)查以了解可能的影響。捕獲這項(xiàng)工作很重要。歷史數(shù)據(jù)庫是記錄用戶學(xué)習(xí)內(nèi)容的自然場所。
另外,如果用戶確定所識(shí)別的代碼是安全可靠的,歷史數(shù)據(jù)庫可以記錄該給定消息應(yīng)該從后續(xù)輸出中被抑制,并且可以記錄抑制該消息的支持理由?;蛘?,如果需要更改識(shí)別的代碼,歷史數(shù)據(jù)庫可以記錄分配給問題的程序故障報(bào)告 (PTR) ID,從而允許問題跟蹤系統(tǒng)和分析工具的歷史結(jié)果之間的可追溯性。當(dāng)該工具檢測到具有關(guān)聯(lián) PTR ID 的消息消失時(shí),可以將其配置為直接通知問題跟蹤系統(tǒng)可以關(guān)閉關(guān)聯(lián)的 PTR 記錄。自動(dòng)化關(guān)閉 PTR 的過程可以顯著減輕通常負(fù)擔(dān)過重的質(zhì)量保證團(tuán)隊(duì)的負(fù)擔(dān)。
靜態(tài)分析作為開發(fā)過程的關(guān)鍵組成部分
隨著應(yīng)用程序變得越來越大和越來越復(fù)雜,高級靜態(tài)分析工具在現(xiàn)代軟件開發(fā)中發(fā)揮著關(guān)鍵作用,它顯著減少了查找可能危及系統(tǒng)可靠性、安全性或安全性的錯(cuò)誤和漏洞所需的工作量。但許多組織尚未充分利用這些工具,通常是因?yàn)閷⑺鼈兗{入日常軟件開發(fā)過程(構(gòu)建、回歸測試和其他步驟)可能存在很高的進(jìn)入障礙。
如前所述,兩個(gè)重要的步驟可以減少這種進(jìn)入障礙:工具與編譯器技術(shù)和歷史數(shù)據(jù)庫的集成。這不僅僅是一個(gè)理論上的提議。CodePeer 是由 SofCheck 和 AdaCore 聯(lián)合開發(fā)的高級靜態(tài)分析工具,作為 Ada 的自動(dòng)代碼審查器。該工具已完全集成到 AdaCore 的 GNAT Pro Ada 開發(fā)環(huán)境中,并可通過 GNAT Programming Studio IDE 調(diào)用。
與編譯器的集成在很大程度上消除了將源代碼移植到分析工具的挑戰(zhàn)。成功編譯源代碼的同一編譯器前端還可以生成高級靜態(tài)分析引擎進(jìn)行更深入分析所需的中間表示。此外,相同的命令行開關(guān)、源代碼結(jié)構(gòu)和 make 文件可用于編譯和靜態(tài)分析代碼。編譯器前端將自動(dòng)處理應(yīng)用程序使用的任何特定于實(shí)現(xiàn)的功能。
降低進(jìn)入壁壘的第二個(gè)主要步驟是與歷史數(shù)據(jù)庫的集成,這使得在大型系統(tǒng)上工作的開發(fā)人員可以專注于他們最近的更改,并將審查以前發(fā)布的遺留代碼中的問題推遲到更合適的時(shí)間。此外,與數(shù)據(jù)庫的集成允許開發(fā)人員記錄審查工具輸出的結(jié)果以及決定隱藏消息或?qū)⑵錃w檔為 PTR 的理由。最后,數(shù)據(jù)庫會(huì)自動(dòng)驗(yàn)證修復(fù)并關(guān)閉 PTR。通過這兩個(gè)步驟,靜態(tài)分析可以成為嵌入式軟件開發(fā)人員工具箱中重要且高效的工具。
審核編輯:郭婷
-
嵌入式
+關(guān)注
關(guān)注
5087文章
19153瀏覽量
306426 -
編譯器
+關(guān)注
關(guān)注
1文章
1638瀏覽量
49197
發(fā)布評論請先 登錄
相關(guān)推薦
評論