胡志誠,莊 毅,晏祖佳
(南京航空航天大學 計算機科學與技術學院,南京 211106) E-mail:zy16@nuaa.edu.cn
在太空輻射環(huán)境中,因為空間輻射或高能粒子撞擊,使計算機系統(tǒng)中的硬件部分如半導體數字電路等受到干擾,這種現象稱為單粒子效應.它的具體表現有單粒子翻轉(SEU,Single Event Upset)等.SEU屬于一種瞬時故障,是由于高能電離粒子對微電子設備(微處理器、半導體存儲器、功率晶體管等)的敏感節(jié)點進行轟擊,使得這些信息邏輯位上發(fā)生單位翻轉的物理現象[1].其發(fā)生的位置和時間,具有隨機性、可恢復性、瞬時性等特點,因此也被稱為軟錯誤[2,3].
由軟錯誤所觸發(fā)的瞬時故障不會對計算機硬件造成永久性損壞,但會對運行在計算機硬件上的軟件帶來不可忽視的影響,比如造成程序陷入死循環(huán)、程序結果錯誤、程序奔潰等后果.在航空航天等領域,軟錯誤的發(fā)生會帶來巨大的經濟損失,甚至災難性的后果.例如,2011國家首顆火星探測衛(wèi)星“螢火一號”,由于空間粒子輻射導致的軟錯誤,未能完成火星探測任務.2016年美國的SUN公司因為存儲器出現軟錯誤而影響了全美大量服務器的正常工作,導致數百萬美元的經濟損失.研究表明,在已發(fā)生的瞬態(tài)故障中,有相當大一部分故障由數據流錯誤引發(fā).
本文致力于解決軟錯誤中的數據流錯誤檢測問題.數據流錯誤是軟錯誤的一個重要類型.當單粒子效應發(fā)生在寄存器、Cache、內存或者其它存儲設備存儲的數據中或者影響到運算單元和數據總線等部件時,就有可能導致程序正常執(zhí)行但輸出結果錯誤,這類錯誤被稱為數據流錯誤.基于軟件實現的數據流錯誤檢測方法主要有兩種:程序冗余[4]和程序斷言[5].在源代碼級數據流錯誤檢測算法的最基本方法是冗余復算,屬于程序冗余.冗余復算一般步驟為:副本變量和原始變量的一致性規(guī)則,根據規(guī)則生成副本變量;將原始程序轉換為由副本變量組成的副本程序;將原始程序和副本程序合并為復算程序;插入檢查和恢復語句,完成加固程序轉換.在基于冗余復算的數據流錯誤檢測方法中,源代碼級已有的方法存在檢測率有待提升且時空開銷大的問題.如何解決檢測率和時空開銷的平衡問題是一個挑戰(zhàn).
本文主要貢獻如下:
1)提出了一種基于深度學習的數據流錯誤檢測方法DEDDL(Data flow Error Detection based on Deep Learning);
2)設計并實現了針對源代碼級的智能分析機制,包括提取程序級代碼中關鍵信息和脆弱變量集合等;
3)提出基于智能分析的數據流錯誤檢測算法DAIA(Detection Algorithm based on Intelligent Analysis),自動添加冗余并插入相關檢測代碼后得到具有檢測功能的容錯程序.
文章還設計實現了基于GDB的故障注入工具GDFI(GDB Fault Injection).通過分析反匯編后,源代碼所使用到的數據寄存器,得到程序使用的數據寄存器集合.從集合中選取任意數量的數據寄存器進行單位或多位翻轉,以驗證本文提出方法的有效性.本文共進行了2000次故障注入實驗,相比于同類的研究工作,本文的方法在時空開銷方面具有更大的優(yōu)勢.并具有獨立于具體編譯器,便于實施,快速部署等優(yōu)點.
現有的數據流錯誤檢測方法主要有硬件方法和軟件方法兩種[6].基于硬件實現的數據流錯誤檢測方法通常需要添加額外的硬件來進行故障檢測.這類檢測方法具有較高的錯誤檢測率和較低的時間開銷,但是卻增加了成本和空間開銷.在宇航級計算機系統(tǒng)和一些商用嵌入式計算機系統(tǒng)中,這樣的開銷是難以承受的.基于軟件實現的數據流錯誤檢測因其具有成本低、靈活度高、開發(fā)效率高等優(yōu)勢,被越來越多地應用到檢測單粒子效應導致的瞬時故障中,同時也成為研究的熱點.
基于軟件的數據流錯誤檢測算法,針對程序的不同層級,從下到上又可分為指令級,源代碼級,進程級和線程級.相比于指令級數據流檢測技術,在高級語言層實現冗余計算機的優(yōu)點是獨立于具體應用平臺和編譯器,實現策略比較容易,而且具體的實現策略還可以由用戶根據需要自由配置和擴展[7].而且,省略了重新分配寄存器和復制內存等底層操作,能夠較為有效地檢測數據總線、寄存器和內存中的軟錯誤.Benso等人設計了一個面向C和C++程序的編譯工具RECOO(Reliable Code Complier)[8].這一工具主要包括代碼重排和變量復制兩種技術,來檢測發(fā)生在寄存器、內存等存儲部件中的數據錯誤.其主要通過縮短程序變量的生命周期以有效減少錯誤發(fā)生概率,最終提高程序運行的可靠性,但卻帶來了巨大的時空開銷.在此基礎上,RECCO還給出了一些優(yōu)化方法,根據變量生命周期以及數據依賴關系,定義變量的權重,從而統(tǒng)計出關鍵變量和代碼段,以此實現有限復制關鍵變量來達到減少開銷的目的.但是,該方法在一定程度上犧牲了檢測率,且造成的時空開銷仍然很大,沒有本質性的改變.
Rebaudengo等人設計并實現了一個轉換工具ThOR(Translator for Reliable Software)[9],可以將導入的源代碼進行變量冗余,以生成具有容錯功能的源代碼程序,即實現了舊源代碼到新源代碼的轉換,具有較高的錯誤檢測率.但是,由于一行高級語言的代碼經過編譯匯編后會對應多條運算指令,這類方法相對于指令級方法來說粒度更粗,且性能開銷也很大.其實驗結果顯示:該方法的平均空間開銷為289%,且運行的平均性能開銷高達262%,遠高于相關指令冗余方法的開銷.
此外,源代碼級的數據流錯誤檢測也有使用數據差異變換的思想.數據差異變換技術是利用差異性因子K來完成對源程序的復算,最終產生一個對應副本.如譚蘭芳等人提出了一種基于差異轉換和冗余復算的錯誤檢測機制,即基于一組差異轉換規(guī)則,將原程序轉換為功能完全一致的冗余程序,通過在特定位置插入比較檢測語句來判斷程序運行過程中是否發(fā)生軟錯誤[10],該方法在錯誤覆蓋率與時空開銷方面均有較大的進步,但仍有改進的空間.
故障注入是驗證檢測效果的重要手段.以往的方法將需要注入故障的指令信息加入故障注入列表,依照列表進行故障注入.這種方法因為需要消耗大量時間而往往代價過高.現有的研究多是通過選擇性故障注入以降低開銷.如Hari等人提出了一種名為Relyzer的故障點分析方法[11],其目的是壓縮故障空間并檢測故障結果或其等效故障,以便選取小部分故障執(zhí)行選擇性故障注入,達到減少故障注入系統(tǒng)開銷的目的.Li等人提出了一種智能故障注入框架SmartInjector[12],該框架能夠在未進行故障模擬的情況下裁剪故障,或者尋找等效故障,從而更有效地壓縮故障空間,提高故障注入效率.Xu等人提出了CriticalFault故障注入框架[13],通過指令級脆弱性分析從故障中刪除良性故障.
基于人工智能的SDC脆弱性分析方法通過提取程序的一系列特征來預測其脆弱性,也是目前研究熱點之一.Bronovetsky等人比較了支持向量機和神經網絡這兩種機器學習方法在預測程序脆弱性時的優(yōu)缺點,提出了一個模塊化程序故障分析的基本框架[14],該框架能夠分析各代碼段的脆弱性并生成漏洞模型,但他們提出的方法僅局限于線型代數應用程序.Lu等人提出了一種經驗模型SDCTune[15]來預測SDC脆弱性,該模型利用決策回歸樹對程序中的存儲指令和比較指令進行脆弱性預測.Laguna等人針對瞬時故障下科學計算機程序中的SOC(Silent Output Corruption)錯誤,提出了一種指令復制技術[16],該技術采用支持向量模型對程序中的SOC脆弱指令進行判定,并對其進行冗余保護,該方法可降低錯誤檢測的性能開銷.但該方法針對的是科學計算程序,普適性不夠好.
綜上所述,雖然在源代碼級數據流錯誤檢測中,已有的方法已能夠取得有效地檢測效果或可觀的時空開銷,但在錯誤覆蓋率與時空開銷降低的平衡問題上仍存在不足.對程序減少代碼冗余處理,提高錯誤檢測覆蓋率的同時保持較低的性能開銷,是當前研究的重點.與指令級容錯技術相比,高級語言級實現冗余復算的優(yōu)點在于實現簡便,不需要重新進行寄存器分配和內存復制等底層操作,就能有效地檢測內存、寄存器、數據總線和算術運算單元等部件中的軟錯誤.而且它獨立于具體應用平臺和編譯器,用戶可以根據需要對冗余復算進行配置和優(yōu)化,以達到可靠性和性能開銷之間的平衡,具有跨平臺可移植性好等優(yōu)點.相關研究表明,對源程序脆弱部分進行分析并結合應用機器學習技術進行預測是提升錯誤檢測率,同時降低性能開銷的一個有效途徑.本文結合深度學習模型,研究源代碼變量脆弱性以及源代碼級軟錯誤檢測方法,以達到提高錯誤覆蓋率和降低開銷的目的.
進行數據流錯誤檢測,需要掌握數據流錯誤在源代碼這一層級的表現形式.對于大多數源代碼級的編程語言,程序語句主要分為兩種:表達式語句和控制語句[10].其中表達式語句包括運算,賦值等操作語句.這些操作語句的共同特點是它們只對程序數據進行操作,不會改變代碼運行的控制流跳轉;控制語句包括循環(huán)、選擇、函數調用和返回等.根據SEU對兩種語句的影響,可觸發(fā)的源代碼級數據流錯誤共分為兩類:一類是軟錯誤發(fā)生在表達式語句中,導致執(zhí)行結果錯誤.這種錯誤是典型的數據流錯誤.另一類是軟錯誤發(fā)生在控制語句的條件運算過程中,從而導致形式上是控制流錯誤,其實本質上是數據流錯誤.因為這是由數據流錯誤的傳遞所引起,這可能導致多種控制流檢測算法失效[10].因此需要大量的源代碼數據,并以此為支撐對獲取的數據進行相關預處理,如圖1所示.數據預處理操作包括源代碼測試集獲取,源代碼變量提取,源代碼基本功能區(qū)域劃分,以及源代碼分析域劃分等4個部分.通過數據層的預處理操作,可以得到數據特征.
圖1 源代碼數據流智能分析機制結構圖Fig.1 Structure diagram of intelligent analysis mechanism of source code data flow
然后,將數據預處理層得出的數據特征,以及源代碼數據集得到的.c文本文件,利用卷積神經網絡中進行訓練.在訓練前,仍要進行數據預處理,這里的預處理主要是處理文件形式以方便導入CNN中進行訓練學習,根據學習模型學習的結果,結合提出的數據流錯誤檢測算法DAIA進行加固.為了驗證加固的效果,進行故障注入實驗,試驗的結果可以為模型提供參考,再次納入到模型的學習過程中.
數據流在源代碼層級的表現載體主要為代碼中定義的數據變量.基于程序要實現的功能不同,不同變量的個數、出現位置,以及同一變量出現的次數、出現的位置都有所不同.而變量的數量和在源代碼中出現的位置往往影響著數據流錯誤的發(fā)生,即不同變量因位置與出現頻率不同,受到軟錯誤的影響也不同.由于源代碼變量的脆弱性分析有利于指導高效費比的軟錯誤檢錯機制的設計,因此本文研究重點放在如何獲取源代碼中的脆弱變量并對其進行有選擇性的備份處理.
(1)
(2)
(3)
由上述定義知,Rget?Rsten.由于程序中的變量存在于程序語句中,因此二者滿足包含關系,即v∈s,B?R.
定義3.定義基本功能區(qū)域(Basic Functional Area,BFA):BFA是在靜態(tài)分析時能夠確定的一組順序執(zhí)行的源代碼序列.程序的數據流只能從BFA第一條語句開始執(zhí)行,并在BFA的最后一條語句結束.在BFA中,除了最后一條語句可以跳出程序或者轉入下一個BFA中,沒有別的語句會使程序控制流跳出程序或者轉入別的BFA;同時,除了BFA的第一條語句,無其它入口語句.集合A={a1,a2,…,ai,ai+1,…,an}表示基本功能區(qū)域,則有:BFA_in=a1,BFA_out=an,next(ai)=ai+1,i 這里所指的跳出程序語句或者轉入其它BFA語句主要是指負責將程序運行信息從內存?zhèn)鬟f給顯示器、文件、鍵盤等輸出設備,如C語言中的error或printf等語句,或者是轉移到下一個BFA的語句.存在于完成不同功能的BFA中的變量具有不同的任務與屬性,可為變量脆弱性識別提供一種重要的特征. 定義4.定義基本分析區(qū)域(Analysis Area,BAA)是由控制流圖CFG中連續(xù)若干個基本功能區(qū)域BFA以及其有向邊組成的有向子圖.程序的控制流從BAA的第一個BFA開始到達最后一個BFA結束.除了最后一個BFA的最后一條語句,沒有其它語句可以使得程序控制流終止或者跳出當前基本分析區(qū)域;同理,除了第一個BAA的第一條語句,沒有其它語句可以進入到該基本分析區(qū)域中.集合M={A1,A2,…,Ai,Ai+1,…,An}表示基本分析區(qū)域.設BAA_in=A1,BAA_out=An,next(Ai)=Ai+1|Ai+2,i 通過基本分析區(qū)域,可以判斷同一變量所處的不同基本功能區(qū)域,即一個變量從創(chuàng)建賦值到最終不再使用所經歷的過程,為變量提供一種重要的特征. 定義5.控制流圖(CFG,Control Flow Graph):CFG是所有基本功能區(qū)域組成的集合G={A1,A2,…,An},定義4可知M?G,由定義1知Bkind?G;它們之間的跳轉調用關系形成的邊的集合E={br1,br2,…,brm}組成的一個有向圖G.控制流圖代表程序可以執(zhí)行的順序準則.其中控制流圖的邊指的是從一個BFA跳轉到另一個BFA的過程.這包含了BFA的第一條語句和最后一條語句,通常包括選擇語句、函數調用語句、循環(huán)語句和返回語句等. 截取嵌入式基準通用測試集合MiBench[17]以及新一代的行業(yè)標準化的CPU測試基準套件SPEC CPU 2006 benchmark[18]中的一部分代碼段進行基本分析區(qū)域的劃分.圖2所示為基本分析域(BAA)的劃分示例.從圖2中可知一個基本功能區(qū)域(BFA)可以劃分為一個最小粒度的基本分析區(qū)域,即最小的虛線框所標注的B3基本功能區(qū)域.較大虛線框中是劃分的一個大的基本分析區(qū)域,包括B2、B3、B4、B5這4個基本功能區(qū)域. 圖2 基本分析區(qū)域BAA示意圖Fig.2 Schematic diagram of Basic Analysis Area 可根據基本分析區(qū)域進行源代碼結構分析以評估源代碼語句的脆弱性,并以此展開源代碼數據集的標注工作.為了更加精確的描述源代碼語句的脆弱性,以及降低基本分析區(qū)域的劃分帶來的時空開銷.設計了以下脆弱性判定規(guī)則并分析規(guī)則合理性. 規(guī)則 1.若同一變量于同一基本功能區(qū)域或基本分析區(qū)域兩次及以上發(fā)生賦值改變時,將該變量加入脆弱性變量集合. 分析:變量的每一次改變,都對應著多條匯編指令與相關數據寄存器的調用,其受到SEU影響的概率則越大,如循環(huán)變量,條件賦值變量等. 規(guī)則 2.當變量定義并賦值后,若該變量出現在3個及以上基本功能區(qū)域或兩個基本分析區(qū)域,則將該變量加入脆弱性變量集合. 分析:當變量出現在3個或者3個以上基本功能區(qū)域或兩個基本分析區(qū)域時,則說明變量在程序的不同位置被使用,屬于多次使用,在匯編指令層級涉及的匯編代碼較多,涉及的寄存器、內存單元等容易受到SEU影響的部件增多,則變量更脆弱. 規(guī)則 3.若源代碼語句中含有脆弱變量,則該源代碼語句為脆弱性語句.即若v∈Bget且v是源代碼語句s中的一部分,則s∈Rget. 分析:當源代碼語句中出現脆弱性變量,則相對于其它不含有脆弱性變量的語句,當程序執(zhí)行到該語句時出現SUE的概率較大,則應列為脆弱性語句.在程序中標明脆弱性語句,用于構造數據集供機器學習模型使用. 規(guī)則 4.所劃分的基本分析域滿足大于一個基本功能區(qū)域的規(guī)則. 分析:根據定義,源代碼程序根據已經劃分好的基本功能區(qū)域,可劃分的基本分析區(qū)域的方式有很多.按照不同的區(qū)域劃分,程序可分成的區(qū)域結構也不同.為了權衡后續(xù)變量冗余開銷與充分發(fā)揮BAA的作用,應使其作用區(qū)別于基本功能區(qū)域. 源代碼的程序特征包括變量類型,出現位置,變量活躍程度,以及其所屬的基本功能區(qū)和基本分析域等特征.本文利用靜態(tài)分析技術[19,20]和詞法分析工具Flex[21],結合Linux環(huán)境下對基于LLVM的編譯器[22]進行的二次開發(fā),對程序進行分析.在編譯器層面,對高級語言翻譯成的中間代碼語言進行整合分析,協助分析源代碼的程序特征. 源代碼的特征提取是DEDDL方法的一項主要內容,設計的特征提取過程如圖3所示.具體包括以下步驟: 圖3 特征提取流程圖Fig.3 Feature extraction flow chart Step 1.進行靜態(tài)分析與詞法分析.根據Flex規(guī)則與LLVM編譯原理知識,對源代碼程序進行分析,得到關鍵變量,標記并區(qū)別于源代碼中其余單詞,例如關鍵字和符號,還包括每個變量的類型,出現的位置等,最終得到變量信息表; Step 2.源代碼與變量信息分析.根據變量信息表統(tǒng)計出的內容,結合定義的規(guī)則,判定并統(tǒng)計源代碼程序中的脆弱變量與脆弱語句; Step 3.源代碼特征標注.利用得到的源代碼程序中脆弱變量與脆弱語句集合,選取MiBench以及SPEC CPU benchmark中部分源代碼進行脆弱性特征標注,得到源代碼訓練數據集. 經歷以上3個步驟后,便可以得到含有含有變量特征的源代碼特征集.其中,變量的位置是變量所處源代碼的行號;變量使用頻次指的是變量被賦值或者用于算術和邏輯運算時改變的情況;變量的生命周期是指同一變量所出現的基本功能區(qū)的個數. 表1是根據圖2劃分基本分析區(qū)域BAA所用程序統(tǒng)計出的3個變量信息表,為了抹去變量名帶來的差異性,用變量n(n=1,2,3)代替變量名.表中列出了每個變量所包含的4個特征:類型、出現位置、所在BFA、所在BAA等.以變量1為例,其類型屬于int型,出現在了源代碼的第3、第14和第16行,這是其在代碼中所處的位置.因為其分屬于兩個BFA和BAA,且在第2個BFA中出現兩次,根據上述脆弱性變量與語句判斷規(guī)則,判定為脆弱性變量,脆弱性值置為1,并納入脆弱性變量集合.與變量1不同,變量2屬于char型,出現在源代碼程序第4行,與變量3的第5行在同BFA與BAA中定義與使用,因其在每個區(qū)域只出現一次,根據錯弱性變量與語句判斷規(guī)則,判定為普通變量,脆弱性值為0. 表1 變量信息表Table 1 Variable information table 本文運用機器學習中的深度學習方法—卷積神經網絡(Convolutional Neural Networks,CNN)[23]來判斷源代碼中的脆弱性語句.設計的處理流程如圖4所示. 圖4 基于CNN的數據處理流圖Fig.4 Data processing flow chart based on CNN 由于卷積神經網絡需要一定量的數據作為分類模型的訓練樣本以及需要設置合理的參數來調節(jié)訓練的時間和結果的準確度.所以首先需要對.c的源代碼數據集進行預處理.經過處理的源代碼文本形成預處理數據集可導入卷積神經網絡進行訓練.在訓練之前,需要配置卷積神經網絡的參數,以達到良好的訓練效果,最終得到語句脆弱性預測模型,該模型能夠判斷語句變量是否為脆弱性語句,為設計數據流檢測算法提供冗余參考. 在配置的CNN對標注好的源代碼訓練集進行訓練后,會得到一個能夠對源代碼文件進行語句脆弱性判斷的分類模型. 如圖5所示,向分類器中導入一個新的.c源代碼程序文件,該分類器可以遍歷文件中的源代碼語句,識別出源代碼中的脆弱性語句,將識別出的結果導出到文本中,以備后續(xù)應用. 圖5 語句判斷過程Fig.5 Sentence judgment process 3.4.1 源代碼文本數據預處理 由于卷積神經網絡需要較大的訓練樣本作為訓練集,并且CNN會從已有樣本的訓練過程中提取自己的特征.因此,使用嵌入式基準測試集合MiBench測試集以及SPEC CPU benchmark中的C代碼作為基準程序集,根據定義的規(guī)則進行文本中語句與變量脆弱性的相關標注與數據清洗,以此作為配置好參數的卷積神經網絡的訓練集.由于源代碼語言不同于普通的自然語言,因此針對源代碼測試集進行自動停用詞提取[24],形成針對源代碼程序的停用詞集合文本.其中,停用詞是指在信息檢索中,為節(jié)省存儲空間和提高搜索效率,并且其存在與否對于自然語言的情感表達不會構成較大影響,在處理自然語言數據(或文本)之前或之后會自動過濾掉某些字詞或者符號. 如圖6給出的一個源代碼預處理示例,從如下幾個方面進行預處理工作:1)對測試集中所有源代碼進行格式整理,調整成統(tǒng)一的編碼風格,包括變量的命名與函數的定義與應用等;2)去掉所有MiBench與SPEC CPU benchmark代碼中的注釋,并對變量名稱的命名規(guī)范進行統(tǒng)一的標準化處理,如文件指針變量統(tǒng)一命名為fp,計數器變量統(tǒng)一命名為counter等.這樣做的原因不同程序中變量的名稱不同,即使所做的變量操作相同,如都進行相同的算術運算與邏輯運算,CNN在進行代碼詞卷積的時候,由于變量的名稱的不同可能會提取不必要的特征;3)在去除停用詞時,用空白符進行替代(即圖中的“□”),這么做的原因是為了提醒此處有內容,并且減少其在CNN訓練過程中對源代碼相關特征提取的影響.值的一提的是,沒有去掉代碼的縮進空格,因為源代碼結構特征的表示形式便是所擁有的縮進數量,用空白符來代替縮進的數量,這為CNN提供了一個很好的數據特征. 圖6 源代碼處理示例Fig.6 Sentence judgment process 3.4.2 基于CNN的數據流脆弱性判斷模型 CNN的基本結構由嵌入層(embedding layer)、卷積層(convolutional layer)、最大池化層(max-pooling layer)、全連接層以及輸出層構成.卷積層和池化層一般會取若干個,采用卷積層和池化層交替設置,即一個卷積層連接一個池化層,池化層后再連接一個卷積層,以此類推.由于卷積層中輸出特征面的每個神經元與其輸入進行局部連接,并通過對應的連接權值與局部輸入進行加權求和再加上偏置值,得到該神經元數值,該過程等同于卷積過程,CNN也由此而得名[25]. 如圖7所示為本文構造的用于處理源代碼的卷積神經網絡圖.從已經實現預處理的源代碼數據文本導入數據.以一段代碼詞文本為例,首先對該段代碼詞進行embeding操作,嵌入為t×k的詞向量矩陣,其中t為代碼詞的數量,k為每次進行卷積操作的代碼詞數量.因為,結合楊等人的工作[26],并參考源代碼文本程序的特點,進行反復的調參實驗,最終確定將嵌入大小設為embeding_size=4.通過embeding操作,可以起到對低維度的代碼詞數據進行升維度的作用,放大不易發(fā)現的特征,并把籠統(tǒng)的特征分離開,這對CNN進行特征提取起到很重要的作用.設wt∈m,其中m為m維代碼詞向量空間,wt為預處理后的源代碼語句第t個代碼詞,則長度為n的源代碼語句可表示為: 圖7 卷積神經網絡構造圖Fig.7 Construction graph of convolutional neural network w1:n=w1⊕w2⊕w3⊕…⊕wt⊕…⊕wn (4) 其中⊕為并置運算符,可連接不同的代碼字符串組成不同的源代碼子語句.接下來,進行特征提取操作.考慮到訓練的時間與準確性,結合MUHAMMAD等的相關工作[27],本文定義了3個不同大小的卷積核k∈{2,3,4},形成多核卷積層,并在每一個卷積核上進行特征映射.其中一個卷積核x∈mk,x應用于長度為k的滑動窗口以產生新的特征At.其中At是由截取的一段代碼詞得到的特征,計算公式為: At=f(x·wt:t+k-1+Δ) (5) At是由第t個代碼詞到第t+k-1個代碼詞上,通過非線性函數f得到的特征.其中Δ∈,作為函數中的偏置項來使用.本文共使用了3種不同的卷積核,因此所產生的語句集合為{w1:k,w2:k+1,…,wn-k+1:n},則由此可產生的特征集合為: A={A1,A2,…,An-k+1} (6) 得到上述獲得的特征后,進行CNN的最大池化操作(Max pooling).最大池化操作對某個卷積核抽取得到的若干特征進行抽取,抽取最大值作為保留值,并將其它的特征值全部丟棄.因為值最大的特征代表該特征是最強的,拋棄其它弱的此類特征.最終,卷積神經網絡將得到的特征進行dropout和softmax操作,將得到最終的二分類——脆弱與正常的結果.本文dropout操作是在CNN訓練過程中對于神經網絡單元按照一定的概率將其暫時從網絡中丟棄.由于是隨機丟棄,故而每一個mini-batch都在訓練不同的網絡,這樣可以減少神經網絡中的過擬合問題.最終通過softmax計算輸出預測結果分別落在脆弱性語句與正常語句兩類中的概率. 本文設計的基于智能分析的數據流錯誤檢測算法DAIA主要包括兩個子算法.算法包括對卷積神經網絡篩選出的脆弱性變量進行冗余,并在合適的位置添加檢測代碼.首先,檢測算法對目標源程序代碼逐行進行詞法分析,結合語法分析生成語法分析樹T,從而能夠過濾出每行代碼中的變量.同時,對得到的脆弱變量集合進行遍歷識別訓練模型得到的脆弱性變量,為添加冗余備份提供相應的參考,并為后續(xù)的語句類型判斷添加標記,詳情見算法1. 算法1.WordTree(Root) 輸入:通過靜態(tài)分析的源代碼語法樹的根結點 輸出:添加冗余備份后的源代碼程序 1.start 2.Search(Root0)//遍歷靜態(tài)分析得到語法分析樹,Root0為Root的根結點 3. FOR(Root中的每一個結點Nod) 4. if(Node存在于脆弱變量集合)則用對變量進行備份. 5. Vd=V;Ed=E;get_tag();//初始化,并冗余,加標記 6.end 在添加完冗余備份之后,需要在程序合適的位置插入錯誤檢測代碼以檢測數據流錯誤.這里涉及對源代碼語句類型的判斷.對源代碼遍歷過程進行細化,給出將原程序的一條語句L轉換為對應的冗余復算語句L′和添加檢測代碼的過程. Step 1.判斷L語句的類型; Step 2.如果L是表達式語句,則對其進行復制,使得L′包含原程序的語句,并對其進行表達式轉換,然后在轉換后的表達式后面加入分號,生成冗余的復算語句,加至L′的尾部; Step 3.如果是選擇語句,則對選擇語句的表達式進行轉換后插入比較檢測語句至選擇語句前面,進行相應檢測處理; Step 4.如果是函數調用和返回語句,則在函數調用和返回前插入比較檢測語句比較參數,并進行相應的例程處理; Step 5.如果是輸出語句,則在輸出語句前插入比較檢測語句,比較輸出數據,并進行相應的例程處理. 冗余復算語句轉換算法的詳細描述見算法2. 算法2.PROCEDURE Handle_L(L) 輸入:有標志的冗余源代碼程序 輸出:具有檢測功能的源代碼程序 1.Start 2. 獲取標志L的類型標識符數組 3.while循環(huán)遍歷數組: 4.if:L為表達式語句: 5.then:對L進行復制,使得L′包含原程序的語句 6. L_record′=Copy_L_record(L的表達式);//復制表達式語句 7. 在L_record′后加入分號,生成冗余的復算語句,加至L′的尾部 8.if:L為選擇語句: 9.then:L_record′=Copy_L_record(L的表達式);//復制表達式語句 10. insert(test_L)//在選擇語句的頭部 11. if:L_record與L_record′的計算結果不相等 12. then: 13. 添加檢測提示語句printf(“error is detected”); 14. printf(location);;//打印出錯位置 15. if:L為if語句 16. Handle_L(if語句的語句體)//遞歸調用 17. if:L為if-else語句 18. Handle_L(if部分的語句體) 19. Handle_L(else部分的語句體) 20. if:L為switch語句 21. Handle_L(case 1); 22. … 23. Handle_L(default); 24. if:L為循環(huán)語句: 25. insert(比較檢測語句)//在循環(huán)體內插入 26. L_record′=Copy_L_record(L的表達式);//復制表達式語句 27. if:L_record與L_record′的計算結果不相等 28. 添加提示語句printf(“error is detected”) 29. Handle_L(循環(huán)語句的語句體); 30. if:L為函數調用和返回語句: 31. insert(比較檢測語句)//在函數調用和返回前,比較參數 32. if:將函數調用及返回的參數變量和影子變量進行比較不相等 33. 添加提示語句printf(“error is detected”); 34. Handle_L(函數體); 35. if:輸出語句: 36. insert(比較檢測語句)//輸出語句前插入,用于比較輸出數據 37. if:將輸出語句的參數變量和影子變量進行比較不相等 38. 添加提示語句printf(“error is detected”); 39. Error_Out();//錯誤處理與程序中斷 40. Handle_L(函數體) 41.end 已有的故障注入實驗方法如LLFI,已經被驗證了可用于模擬寄存器硬件故障的準確性[28].然而,LLFI基于LLVM編譯器框架,工作在LLVM IR指令層,不適用本文源代碼級數據流錯誤檢測的故障注入.鑒于源代碼數據流錯誤發(fā)生的載體主要針對的是源代碼中的數據變量,以及為了提高故障注入的精準度和注入效率,針對GDB(GUN Project debugger.https://www.ogur.org)調試工具進行二次開發(fā),構建了故障注入工具GDFI(GDB Fault Injection),使其能夠具有下列功能: 1)自動分析獲取程序運行時的虛擬地址空間范圍; 2)統(tǒng)計使用到的數據寄存器如32位的EXP,64位的AXP等的名稱與出現的頻率; 3)可根據分析的結果以及使用者對注入故障的要求,進行針對性的故障注入等. 總體故障注入實驗流程如下:首先,加固后的源代碼通過編譯器生成匯編代碼;其次,匯編代碼再通過匯編器,生成機器語言目標文件;然后,通過連接器將生成的機器語言目標文件鏈接起來生成可執(zhí)行程序;最后,對可執(zhí)行程序進行故障注入,統(tǒng)計分析加固代碼、故障注入和運行結果. 本文使用Tensorflow[28]這一機器學習框架來構建卷積神經網絡CNN.該框架已被證明可為多種語言提供接口,來開展人工智能的研究工作,尤其在分布式機器學習與海量數據挖掘方面表現尤為突出[29].依據3.1與3.2節(jié)的源代碼關鍵特征信息選取的相關定義與規(guī)則,以及使用基于GDFI的腳本程序進行故障注入,綜合以上內容,將Mibench與SPEC CPU 2006 benchmark中的.c測試集程序預處理為符合規(guī)則要求的數據集合,最終共整合收集了3000條脆弱性標記的測試集樣本.部署以上相關環(huán)境使用的操作系統(tǒng)為ubuntu18.04,編譯器為gcc-7.0.0. 將整理的源代碼數據集以80%和20%的比例分成兩組.其中80%組作為訓練集,20%組作為驗證集.依據3.3節(jié)提出的基于卷積神經網絡的處理模型,構造卷積神經網絡.模型的參數如第3.3.2節(jié)所示,包括卷積核尺寸k=2,3,4,學習率learning_rate=1e-3,保留比例dropout=0.5等. 圖8為監(jiān)視Tensorflow框架搭建CNN訓練數據的追蹤圖.圖的左右為互補,橫軸代表訓練的數據量,縱軸為訓練的準確率,用以對訓練效果的描述.從圖8中可以看出,隨著訓練數據量的不斷增大,訓練的準確度在不斷的提升,訓練的丟失率在不斷下降,直至趨于平穩(wěn),這也驗證了配置卷積神經網絡參數的合理性. 圖8 CNN訓練過程追蹤圖Fig.8 CNN training process tracking chart 根據上文提出的方法,進行變量脆弱性預測實驗.根據3.1中定義的規(guī)則1-4與故障注入結果來衡量源代碼變量脆弱性預測的準確率.利用逆向工程工具IDA(Interactive Disassembler.https://www.Ida2020.org),將編譯執(zhí)行的可執(zhí)行程序對應到源碼,以分析程序發(fā)生錯誤對應的具體源碼位置,進而找出出錯變量的位置.將本文提出的方法DEDDL得到的脆弱變量的SDC錯誤率與不采用任何加固方法的故障注入實驗(NFI)得到的程序中變量的SDC錯誤率作對比,其中變量SDC率為發(fā)生SDC錯誤的變量占源代碼中所有變量的比率,對比結果如圖9所示. 由圖9中可見,通過故障注入實驗得到的矩陣乘法、傅里葉變換、冒泡排序與迪杰斯特拉等4個測試程序的變量平均SDC率為26.5%,略高于本文DEDDL方法預測得到的24.5%,預測效果良好.分析DEDDL預測值略低NFI的原因是因為不采用任何檢測加固方法進行的故障注入實驗具有較高的隨機性,不考慮程序結構與變量出現頻次等原因.而本文提出的DEDDL方法強化了變量脆弱性的約束條件,即強調程序結構與變量相關的諸多因素.這為故障檢測,減少時空開銷提供了的基礎. 圖9 變量SDC預測準確率對比Fig.9 Comparison of prediction accuracy of variable SDC 對數據流錯誤檢測方法的評估主要通過SDC錯誤檢測率和性能開銷兩個指標.對應用了本文提出的檢測算法的加固程序進行2000隨機故障注入實驗,來驗證方法的有效性與相關性能開銷.將注入故障后對程序最終運行結果的影響歸納為5類: 1)輸出正確(Correct Output):注入故障沒有影響程序的正常運行,且輸出的結果與未進行故障注入所輸出結果相同. 2)輸出不正確(Incorrect Output):程序能夠順序運行至結束,但是輸出了不正確的結果. 3)程序異常(Exception):程序不能夠正常結束,觸發(fā)操作系統(tǒng)保護機制或不正常終止. 4)陷入死循環(huán)(Dead Loop):故障注入導致程序無法正常終止或不能在預期的時間完成并結束. 5)錯誤檢測(Error Detected):程序輸出錯誤但是被檢測算法檢測到. 通常,因為程序異常會觸發(fā)操作系統(tǒng)的保護機制,將出錯程序移交內核態(tài)進行異常處理,因此程序異常的結果歸入方法的整體檢測率.定義漏檢率為輸出不正確比率與陷入死循環(huán)比率的總和,檢測率為輸出正確比率、程序異常比率、錯誤檢測比率的總和. 表2展示了對4個測試程序:傅里葉變換,冒泡排序,快速排序,矩陣乘法等進行故障注入的結果.其中2-6行展示了程序在運行過程中表現的5種不同轉態(tài).第7行展示了輸出不正確與陷入死循環(huán)情況的總和.第8行則為上文分析的檢測率的總和.從表中可以看出,應用了本文提出的方法后,測試程序在注入故障后運行,平均檢測率能達到98.3%.圖10為SIDFT[30]方法與本文提出的方法在3個典型測試程序上檢測率的對比圖,從圖中可以看出,兩種方法在故障注入后,運行程序的檢測率上具有十分相似的效果. 表2 故障注入實驗結果Table 2 Experimental results of fault injection 圖10 兩種方法的檢測率對比Fig.10 Comparison of detection rate of two methods 在開銷方面,如表3所示.傳統(tǒng)的ThOR方法采用的是簡單的變量復制思想來實現數據流錯誤檢測,該方法在變量每次更新后都需要重新更新變量副本值,在進行變量讀操作時又需要讀取副本變量進行比較操作.因此其平均時間與空間開銷都十分巨大,分別達到了262%與289%.而SIDFT方法是基于語句復制,只需要在定義的無輸出效應基本塊的尾部對參數或者分支進行比較,相較于ThOR插入的比較檢測語句要少很多,其平均時間空間開銷分別為120.4%和131.1%.本文提出的方法采用機器學習識別變量的脆弱性,只需在關鍵部位插入檢測代碼,因此平均時間開銷與代碼開銷在SIDFT的基礎上又有明顯的下降,分別只有119.1%和128.2%,體現出本文提出方法在不損失檢測率的情況下,在空間與時間開銷方面的優(yōu)越性. 表3 數據流錯誤檢測方法開銷比較Table 3 Cost comparison of data stream error detection methods 本文提出了一種基于深度學習的數據流錯誤檢測方法.不同于指令級數據流錯誤檢測方法,本文的方法針對程序級代碼進行分析,能夠預測識別出源代碼中變量的脆弱性.并且根據該方法的預測結果,文章針對性的提出了基于智能分析的數據流錯誤檢測算法,可自動添加具有復算冗余和有檢測功能的語句,實現自動對程序數據流錯誤的檢測.本文還設計并實現了基于GDB的數據流故障注入工具GDFI可用來模擬數據流錯誤進行實驗,以驗證本文提出方法的有效性.實驗結果表明,本文提出的方法,在保持較高錯誤檢測率的同時,相對于已有的數據流錯誤檢測算法擁有較低的時空開銷;并具有獨立于具體編譯器,便于實施,快速部署等優(yōu)點.3.3 源代碼的脆弱性特征提取
3.4 基于CNN的變量脆弱性判斷
4 基于智能分析的數據流錯誤檢測算法
5 實驗與結果分析
5.1 故障注入工具與實驗流程
5.2 變量脆弱性實驗與分析
5.3 數據流錯誤檢測實驗與分析
6 結 論