專利名稱:一種程序控制流錯誤檢測方法
技術領域:
本發(fā)明涉及一種針對程序控制流錯誤的檢測方法,尤其是對在空間輻射環(huán)境下由硬件瞬態(tài)故障所導致的控制流錯誤進行檢測的方法。
背景技術:
空間探測活動投入大、風險高,對計算可靠性有著極高的要求。太空中影響空間探測器安全的主要因素是宇宙射線的輻射,因為宇宙環(huán)境中存在大量包括電子、質子、粒子和重離子在內的高能粒子,當由這些粒子構成的宇宙射線轟擊航天計算機的半導體電路時,就可能導致PN結構中的存儲電量發(fā)生瞬態(tài)變化,這種瞬態(tài)故障通常也被稱為單粒子效應SEE(Single Event Effect)。雖然單粒子效應一般不會對硬件設備造成持久傷害,但是卻可以通過改變傳輸信號和存儲單元值等方式影響系統(tǒng)的正常運行,嚴重時甚至會造成系統(tǒng)崩潰,所以一直是航天計算機所面臨的最主要威脅之一。而現(xiàn)代處理器逐步采用深亞微米制造工藝,在性能得到大幅提高的同時,處理器對于能引起瞬態(tài)故障的各種噪聲干擾也變得越來越敏感,同時單芯片所集成的晶體管數(shù)呈指數(shù)級增長,也使得芯片整體的瞬態(tài)故障率快速增加。當前,在繼性能和功耗之后,瞬態(tài)故障所導致的處理器可信性問題已日益成為業(yè)界關注的熱點。
硬件瞬態(tài)故障對系統(tǒng)可靠性影響的具體體現(xiàn)可分為數(shù)據(jù)流錯誤和控制流錯誤。數(shù)據(jù)流錯誤主要指故障影響應用程序使用的寄存器和存儲器中的數(shù)據(jù)等;而控制流錯誤指的是故障改變了程序正常執(zhí)行軌跡,例如一條存儲指令被SEE篡改成分支指令,無條件跳轉指令的目標地址被瞬態(tài)故障臨時修改。當發(fā)生控制流錯誤后,程序行為會變得復雜且難以預測有時由于執(zhí)行了非法的指令而被操作系統(tǒng)或底層硬件檢測出,也有可能導致程序進入死循環(huán),最壞的情況是程序正常退出而執(zhí)行結果卻是錯誤的。以往的實際經驗和各種故障注入實驗結果表明,控制流錯誤占瞬態(tài)故障所引起的各種系統(tǒng)錯誤總數(shù)的33%~77%。所以,對于航天計算機這樣的高可靠計算機系統(tǒng)來說,必須具備一定的控制流錯誤檢測能力。
在空間環(huán)境中,為了防止空間輻射的影響,航天計算機一般使用經過特殊硬件工藝設計與加工的抗輻照器件。抗輻照器件通過硬件冗余實現(xiàn)容錯,具有很高的可靠性,能夠有效解決空間輻射所導致的硬件瞬態(tài)故障問題。但是抗輻照器件設計非常復雜、研制周期長、產業(yè)規(guī)模和產量都很小、價格非常昂貴,而且抗輻照器件的性能通常落后于同時代的商用器件COTS(Commercial Off-The-Shelf)很多代。此外,目前專門針對控制流錯誤也已提出一些通過改造硬件實現(xiàn)的檢測技術,例如Watchdog輔助處理器技術。Watchdog技術先對程序的控制流結構進行分析,并為這種高層結構附上標簽,然后由Watchdog處理器在運行時監(jiān)測主處理器產生的總線事務。但是這種技術對于具有緩存的現(xiàn)代主處理器無法使用,除非協(xié)處理器是主處理器的一部分??偟膩碚f,基于硬件實現(xiàn)的容錯技術或需要修改硬件的體系結構,或要開發(fā)和配置具有檢錯能力的專門設備,實現(xiàn)成本太高是主要問題。
計算機發(fā)展的歷史表明,很多原本用硬件實現(xiàn)的方法同樣可以用軟件來實現(xiàn),在COTS微處理器上,通過實現(xiàn)面向硬件瞬態(tài)故障的軟件容錯技術可以彌補COTS器件在容錯能力方面的不足。國內外已經開展很多實驗探討在空間環(huán)境中應用COTS處理器,結果表明面向硬件故障的軟件容錯方法可以有效提高基于COTS器件的空間計算機的可靠性,能夠很好地應對空間輻射的影響,同時在COTS器件上利用軟件容錯方法所實現(xiàn)的性能可以比抗輻照器件高一個數(shù)量級,而成本卻要降低一個數(shù)量級。所以,隨著計算機硬件資源的極大豐富,以犧牲部分性能來換取較高的可靠性已成為可能,并且軟件容錯方法在成本、功耗和靈活性方面都有擁有巨大的優(yōu)勢。實際上,基于COTS器件的軟件容錯方法目前已經成為各國航天領域的核心機密技術之一! 針對控制流錯誤,目前軟件容錯方法通常是以基本塊為單位,在程序正常指令流中插入一些額外指令以實現(xiàn)對程序的控制流的校驗。一個基本塊是一個依次順序執(zhí)行的指令序列,其中除最后一條指令外其他指令都不能是程序控制指令(程序控制指令指能夠改變程序原來執(zhí)行順序的指令,通常包括條件分支指令、無條件跳轉指令、函數(shù)調用指令和函數(shù)返回指令等),除第一條指令外其它指令都不能是程序控制指令的轉移目標?;诨緣K,程序可以表示為由基本塊以及連接基本塊之間的有向邊所構成的控制流圖,其中的有向邊表示實際的程序控制流轉移。如果從基本塊Bi到基本塊Bj有一條邊,則表示程序中存在Bi到Bj的路由,Bi記為Bj的前驅基本塊,Bj記為Bi的后繼基本塊。
程序運行時,在執(zhí)行一個指令后會轉向執(zhí)行另一條指令,這個過程稱為一次控制流轉移?;诨緣K和程序控制流圖,合法的控制流轉移有下面兩層含義 1)基本塊內部控制流轉移發(fā)生在一個基本塊內部。由于基本塊內部指令順序執(zhí)行,所以除了塊最后一條指令外,每個指令只有唯一的后繼指令。
2)基本塊之間控制流轉移發(fā)生在基本塊之間。對于基本塊的最后一條指令,其后繼指令可能有多個,但都必須是控制流圖中所屬基本塊的后繼基本塊的第一條指令。
除此之外的所有控制流轉移都是非法的,控制流錯誤檢測技術的目標就是要盡可能高效地檢測出所有非法的控制流轉移。
當前,軟件實現(xiàn)的控制流檢測方法通常采用基于基本塊的標簽分析法(SignatureAnalysis)?;緣K標簽是基本塊的數(shù)字標識,在預處理(如編譯)時為每個基本塊分配唯一的標簽(稱為靜態(tài)標簽AS,Assigned Signature),程序在運行過程中根據(jù)當前控制流由插入的檢測指令維持一個標簽(稱為動態(tài)標簽DS,Dynamic Signature),然后將兩個標簽進行比較,匹配則說明控制流沒有被破壞,否則表示控制流出現(xiàn)了錯誤。
美國斯坦福大學提出的CFCSS方法就是這方面的典型代表。作為針對基本塊之間的控制流錯誤進行檢測的方法,CFCSS在編譯時為每個基本塊生成一個靜態(tài)標簽,并且為每個基本塊計算與其前驅基本塊的靜態(tài)標簽之間的異或差異值D。在運行時,CFCSS使用一個通用寄存器G保存產生的動態(tài)標簽。當進入一個基本塊后,先將G與當前基本塊的D值異或運算產生新的動態(tài)標簽。由于運算之前G值等于前驅基本塊的靜態(tài)標簽,所以運算得到的結果應該與當前基本塊的靜態(tài)標簽相等,否則說明檢測到控制流錯誤。其他的標簽分析法的基本原理與CFCSS相同,只是在基本塊標簽和檢測指令序列的設計方面各有區(qū)別,由此導致錯誤檢測率和性能消耗等方面存在差異。
但是總的來說,基于軟件實現(xiàn)的控制流錯誤檢測方法目前主要存在以下幾個方面的問題 (1)存在檢測盲點已有方法能夠檢測出絕大部分的控制流錯誤,但是各種方法也都不同程度地存在檢測盲點。例如CFCSS無法檢測從一個基本塊的內部直接跳轉到其后繼基本塊的頭部的控制流錯誤。單粒子效應可能修改控制流指令本身,例如把不等于分支指令變成等于分支指令,這導致條件判斷指令本應執(zhí)行THEN分支,結果錯誤地跳轉到ELSE分支。由于這種錯誤分支在程序控制流結構上是合法的,但語義卻是錯誤的,通常被稱為偽分支。偽分支會導致程序正常退出而執(zhí)行結果卻是錯誤,所以危險性很大,同時它也是控制流錯誤檢測中的難點,目前很多方法不能解決這個問題。
(2)基本塊內部的控制流錯誤檢測已有的控制流檢測方法主要關注基本塊之間的控制流正確性,而對于基本塊內部的控制流錯誤并沒有比較完善的解決辦法。有方法在靜態(tài)分析時對基本塊包含的指令進行計數(shù),然后運行時每執(zhí)行一條指令則執(zhí)行一條將計數(shù)減1的指令,當執(zhí)行到基本塊出口時,計數(shù)器的值應該為0。但這種方式增加的指令與基本塊本身包含的指令一樣多,對程序性能影響顯然太大。
(3)過程間的控制流錯誤檢測這是控制流檢測中的一個難點問題。有的方法在實現(xiàn)時不考慮過程間的控制流檢測,即不把函數(shù)調用指令作為劃分基本塊的一個依據(jù);有的方法雖然實現(xiàn)了過程間的控制流錯誤檢測,但是難以處理嵌套調用和遞歸調用等復雜情況。
(4)無法解決可靠性和性能之間的矛盾為了解決檢測盲點,有些方法設計了一些復雜的檢測指令序列。但是由于加入了過多的檢測指令,對程序性能的影響也隨之增大,甚至檢測指令自身受瞬態(tài)故障影響而出錯的概率也變大。
因為已有軟件實現(xiàn)的控制流錯誤檢測方法存在上述問題,必須研究檢測效率更高而對程序本身性能影響較小的控制流錯誤檢測方法。
發(fā)明內容
本發(fā)明要解決的技術問題是克服已有方法存在的檢測盲點(如偽分支),提高控制流錯誤的檢測率,解決基本塊內部的控制流檢測和過程間的控制流檢測等難點問題,并且對程序本身性能的影響較小。
為了解決上述技術問題,本發(fā)明提出的技術方案為首先,基于程序匯編代碼標識基本塊并確定基本塊之間的路由關系;然后根據(jù)用戶對基本塊內部控制流檢測的需求和基本塊的構成特征,確定單個基本塊最多需要多少條內部控制流檢測指令,在此基礎上設計基本塊標簽,并為每個基本塊分配唯一的靜態(tài)標簽;最后在程序中的每個基本塊的頭部、內部和尾部分別插入檢測指令。
具體技術方案為 第一步,通過編譯器將需要進行控制流錯誤檢測的程序編譯成匯編代碼,例如在GCC中可以使用‘-S’參數(shù)生成匯編程序。
第二步,基于程序匯編代碼標識出程序的基本塊并確定基本塊之間的路由關系,具體細分為三個步驟 2.1依次遍歷程序匯編代碼的指令序列,根據(jù)指令操作碼字段識別出程序控制指令(程序控制指令指能夠改變程序原來執(zhí)行順序的指令,通常包括條件分支指令、無條件跳轉指令、函數(shù)調用指令和函數(shù)返回指令等)。然后基于程序控制指令標注基本塊的入口指令,具體方法為所有函數(shù)的第一條指令標注為基本塊的入口指令;對于條件分支指令和無條件跳轉指令,分支或跳轉的目標指令標注為基本塊的入口指令;所有程序控制指令的后繼指令標注為基本塊的入口指令。在標注入口指令的同時,還根據(jù)指令的操作數(shù)字段識別出程序中所有使用過的寄存器,得出哪些寄存器還沒有被程序使用過。
2.2重新依次遍歷匯編程序的指令序列,將一個被標注為基本塊入口指令到下一個入口指令之間的指令劃分為一個基本塊。如果基本塊的入口指令有標識符,就以標識符作為基本塊的名稱?;緣K以函數(shù)為單位按照在程序代碼中出現(xiàn)的順序列表組織,形成函數(shù)基本塊列表,且在函數(shù)基本塊列表中,第一個基本塊標記為函數(shù)入口基本塊,最后一個基本塊被標記為函數(shù)退出基本塊。所有函數(shù)基本塊列表構成程序總的基本塊列表。
2.3依次遍歷匯編程序的所有基本塊,按照基本塊的最后一條指令確定基本塊之間的路由關系,具體方法如下如果基本塊最后一條指令是條件分支指令和無條件跳轉指令,則根據(jù)指令的分支或跳轉目標地址查找對應的目標基本塊,在程序對應的控制流圖中從當前塊向該目標基本塊劃一條有向邊;如果基本塊的最后一條指令是條件分支指令或普通指令(即不是無條件跳轉指令、函數(shù)調用指令和函數(shù)返回指令),則在控制流圖中從當前塊向其直接后繼基本塊劃一條有向邊;如果基本塊最后一條指令是函數(shù)調用指令,則在控制流圖中從當前塊向被調用函數(shù)的入口基本塊劃一條有向邊,并且從被調用函數(shù)的退出基本塊向當前塊在所屬函數(shù)基本塊列表中的下一個塊劃一條有向邊,表示函數(shù)返回的控制流轉移。
第三步,根據(jù)用戶對基本塊內部控制流錯誤檢測的需求和基本塊的構成特征,設計基本塊標簽(含靜態(tài)標簽和動態(tài)標簽)的格式,并為每個基本塊分配唯一的靜態(tài)標簽。基本塊內部控制流錯誤是很多控制流檢測方法的檢測盲點,但是基本塊包含的指令數(shù)有限,控制流錯誤導致恰好轉移到基本塊自身的概率非常小,即內部控制流錯誤發(fā)生概率比較低。如果每執(zhí)行一條指令就進行一次內部控制流校驗,成本顯然太高。一般情況下基本塊所包含的指令數(shù)越多,則出現(xiàn)內部控制流錯誤的概率越大,所以采用一種可配置的方法——當基本塊內部的指令計數(shù)每超過閾值Ω時才進行一次內部控制流校驗,Ω由用戶根據(jù)可靠性、性能的需求及程序運行環(huán)境自定義。具體步驟包括 3.1依次遍歷程序中所有基本塊,根據(jù)基本塊的規(guī)模(即包含指令的條數(shù))對閾值Ω的比例∑,計算在該基本塊的內部檢測指令計數(shù)(即需要多少條內部控制流檢測指令),該內部檢測指令計數(shù)等于
再根據(jù)程序中每個基本塊的內部檢測指令計數(shù),得到整個程序中所有基本塊最大的內部檢測指令計數(shù)。
3.2根據(jù)程序中所有基本塊最大的內部檢測指令計數(shù),設計基本塊標簽?;緣K標簽由基本標簽編碼和用于基本塊內部控制流檢測的編碼兩部分組成。每個基本塊的基本標簽編碼是該基本塊在程序總基本塊列表中的序列號的二進制編碼,是唯一的;而基本塊內部控制流檢測的編碼則是該塊所需的內部控制流檢測指令計數(shù)的二進制編碼,占整個基本塊標簽的最末幾位。在基本塊標簽的末端為塊內部控制流檢測分配
位編碼,N為3.1步分析得出的整個程序中一個基本塊中最多需要的內部檢測指令條數(shù)。
3.3依次為每個基本塊分配唯一的靜態(tài)標簽。所有靜態(tài)標簽中用于基本塊內部控制流檢測的編碼都保持為0。
3.4步驟2.1已確定有哪些寄存器還沒有被程序使用,從這些空閑寄存器中選擇四個寄存器分別定義為DSR、ASR、SVR和RAR,分別保存程序運行過程中產生的動態(tài)標簽DS、靜態(tài)標簽AS、標簽差異值SV(Signature Variance)和函數(shù)調用返回地址RA(ReturnAddress)。其中,SV是兩個基本塊的靜態(tài)標簽異或運算的結果,具體表征了實際控制流在基本塊之間的轉移。如果程序沒有四個空閑寄存器可供使用,則通知用戶無法實現(xiàn)控制流錯誤檢測,用戶可以選擇采用編譯器的其它寄存器分派策略以重新生成匯編程序。
第四步,依次在程序每個基本塊的頭部、內部和尾部分別插入相應指令,因為程序基本塊是以函數(shù)為單位列表組織,插入控制流檢測指令時也是以函數(shù)為單位進行。具體步驟包括 4.1進入一個函數(shù)后,首先針對函數(shù)入口基本塊(記為Bentry)和函數(shù)退出基本塊(記為Bexit)進行特殊處理在調用函數(shù)的基本塊(記為Bcaller)把返回的目的基本塊(即在調用函數(shù)的基本塊列表中當前基本塊的下一個塊,記為Breturn)的靜態(tài)標簽通過寄存器RAR傳遞給被調用函數(shù),被調用函數(shù)的Bentry塊把RAR中的數(shù)據(jù)寫到分配給本函數(shù)的棧區(qū)間,最后被調用函數(shù)的Bexit塊把保存的Breturn靜態(tài)標簽從棧區(qū)間重新讀出,實現(xiàn)對過程間控制流的跟蹤。具體包括三個步驟 4.1.1首先確定分配給本函數(shù)的棧區(qū)間中是否有空閑單元可用于存放RAR中的數(shù)據(jù)。通常編譯器為每個函數(shù)實際分配的棧區(qū)間會有一些空閑區(qū)域,如果確實沒有空閑區(qū)域,則修改Bentry塊入口處用于分配棧區(qū)間的指令,使得該函數(shù)的棧區(qū)間有一個機器字長的空閑存儲單元可用于控制流檢測使用。例如把指令“sub $sp=$sp,36”改成“sub $sp=$sp,40”,可以將函數(shù)的棧區(qū)間增加4個字節(jié)($sp是棧地址寄存器)。然后從函數(shù)的棧區(qū)間的空閑區(qū)域中,選擇其中一個機器字長的存儲單元,記為mem。
4.1.2在Bentry塊分配棧區(qū)間的指令后加入指令“store RAR,mem”,表示把RAR中的數(shù)據(jù)寫到分配給本函數(shù)棧區(qū)間的mem單元之中。
4.1.3如果Bentry塊入口處修改了分配棧區(qū)間的指令,則相應修改Bexit塊入口處回收棧區(qū)間的指令,使得函數(shù)棧區(qū)間的分配和回收保持一致。例如把指令“add $sp=$sp,36”改成“add $sp=$sp,40”。
4.2在當前基本塊頭部(即基本塊第一條指令之前,對于函數(shù)入口基本塊來說則在4.1.2步驟的指令“store RAR,mem”之后)插入指令“xor DSR=DSR,SVR”,表示將動態(tài)標簽與標簽差異值異或運算生成新的動態(tài)標簽。進入一個基本塊時,寄存器DSR值應該等于其前驅基本塊的靜態(tài)標簽,SVR中應該是前驅基本塊的靜態(tài)標簽與當前基本塊的靜態(tài)標簽經過異或運算得到的標簽差異值SV。所以,如果當前基本塊不需要進行內部控制流檢測,指令“xor DSR=DSR,SVR”運算結果會使得DSR值等于分派給當前基本塊的靜態(tài)標簽,否則DSR值應該等于當前基本塊的靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù)。
4.3按照設定的Ω值,在基本塊內部插入將DSR減1的內部控制流檢測指令。依次遍歷基本塊的指令序列,每隔Ω大小的指令數(shù)就插入一條內部控制流檢測指令“sub DSR=DSR,1”,直到剩下的指令總數(shù)不大于Ω值。這樣做的結果是根據(jù)Ω參數(shù)將基本塊劃分為更小的區(qū)域,每個小區(qū)域中包含指令數(shù)小于等于Ω,然后在每兩個區(qū)域之間插入的內部檢測指令可以確保執(zhí)行基本塊內部指令時必須經過這些點。如果因為發(fā)生內部控制流錯誤而繞過這些內部檢測指令,直接從一個區(qū)域跳轉另一個區(qū)域,那么執(zhí)行到塊尾時動態(tài)標簽中用于塊內檢測的幾位肯定不都為0,即DSR與當前基本塊的靜態(tài)標簽AS肯定不同,將會檢測到該錯誤。對于那些本身包含指令的數(shù)目小于Ω值的基本塊則不需要進行內部控制流檢測,基本塊內部保持不變。
4.4當執(zhí)行到一個基本塊的末尾時,動態(tài)標簽寄存器DSR的值應該等于分配給該塊的靜態(tài)標簽。在基本塊的尾部(即基本塊最后一條指令之后,如果最后一條指令是程序控制指令,則在該程序控制指令之前),按照基本塊的類型將DSR與后繼基本塊的靜態(tài)標簽進行異或運算,產生新的標簽差異值SV。具體方法如下 4.4.1如果當前基本塊既不是函數(shù)調用基本塊又不是函數(shù)退出基本塊,而且只有一個后繼基本塊,設該后繼基本塊靜態(tài)標簽為ASnext,則在當前基本塊的尾部插入指令“xorSVR=DSR,ASnext”,表示直接把后繼塊的靜態(tài)標簽值ASnext與DSR進行異或運算,求得新的標簽差異值后賦給SVR。如果后繼基本塊需要進行塊內控制流檢測,ASnext等于后繼基本塊靜態(tài)標簽加上相應的內部控制流檢測指令計數(shù)。
4.4.2如果當前基本塊的最后一條指令是分支指令(即有兩個合法后繼基本塊),則采取分支預測方法提前判斷將要執(zhí)行哪條分支,然后把所預測分支對應的后繼基本塊的靜態(tài)標簽與DSR進行異或運算,求得新的標簽差異值SV。具體方法是設ASthen和ASelse分別表示分支條件滿足和不滿足的目的基本塊靜態(tài)標簽。首先在當前基本塊的尾部插入指令“xor SVR=DSR,ASthen”,表示先假設分支條件滿足,將DSR與ASthen進行異或運算求SVR值。然后在“xor SVR=DSR,ASthen”之后插入分支預測指令“br L1′,brcond”,分支條件“brcond”與實際的分支指令相同,而分支的目標“L1′”必須是一個沒有在程序中出現(xiàn)過的語句標示符,具體指向4.5步驟即將插入的校驗DSR數(shù)據(jù)的指令。最后在“br L1′,brcond”之后插入指令“xor SVR=DSR,ASelse”,表示將DSR與分支條件不滿足的目的基本塊的靜態(tài)標簽進行異或運算求得SVR值。如果分支條件滿足,分支預測指令“br L1′,brcond”執(zhí)行結果將使得程序跳過指令“xor SVR=DSR,ASelse”,即SVR中的值是DSR與ASthen異或運算的執(zhí)行結果,否則SVR中的值是DSR與ASelse異或運算的運算結果。如果后繼基本塊需要進行內部控制流檢測,則ASthen或ASelse分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù)。
4.4.3如果當前基本塊是函數(shù)調用基本塊,其后續(xù)執(zhí)行的基本塊是被調用函數(shù)的入口基本塊。先在當前基本塊的尾部插入指令“xor SVR=DSR,AScallee”,實現(xiàn)DSR與被調用函數(shù)入口基本塊靜態(tài)標簽AScallee的異或運算,并把標簽差異值賦予SVR。然后在“xor SVR=DSR,AScallee”之后插入指令“movRAR=ASreturn”,表示把函數(shù)調用返回的目的基本塊(即當前基本塊在所屬函數(shù)的基本塊列表中的下一個塊)的靜態(tài)標簽ASreturn賦予寄存器RAR。同樣,如果后續(xù)執(zhí)行的被調用函數(shù)的入口基本塊或者函數(shù)返回的目的基本塊需要進行基本塊內部的控制流檢測,則AScallee或ASreturn分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù)。
4.4.4如果當前基本塊是函數(shù)退出基本塊,其后續(xù)執(zhí)行基本塊的靜態(tài)標簽由4.1.2步驟存放在本函數(shù)棧區(qū)間的mem存儲單元中。先在當前基本塊的尾部插入指令“l(fā)oad RAR,mem”,表示從mem中讀出數(shù)據(jù),寫回到寄存器RAR中。然后在“l(fā)oad RAR,mem”后插入指令“xor SVR=DSR,RAR”,表示將DSR與RAR進行異或運算求新標簽差異值。最后在“xor SVR=DSR,RAR”后插入指令“clear mem”,表示將使用的內存單元mem中的數(shù)據(jù)清除。
4.5在基本塊的尾部加入校驗DSR數(shù)據(jù)的指令“br faultDet,DSR?。紸Scurrent”,其中AScurrent表示分派給當前基本塊的靜態(tài)標簽。當正常執(zhí)行到基本塊尾部時,寄存器DSR中的值應該等于AScurrent,如果不相同則表示檢測到控制流錯誤,則轉向錯誤處理例程。該指令放在最后插入可以防止出現(xiàn)在校驗指令之后再改變控制流的檢測盲點。但如果基本塊的最后一條指令是程序控制指令,那么校驗DSR數(shù)據(jù)的指令仍要在程序控制指令之前插入。
第五步,通過編譯器,將插入了控制流檢測指令的匯編程序重新匯編并鏈接,生成可執(zhí)行的實現(xiàn)控制流錯誤檢測的程序。
與已有的控制流檢測方法相比,采用本發(fā)明可以達到以下技術效果 (1)本發(fā)明是一種純軟件方法,不需要修改底層機器硬件。而且本發(fā)明是通過在程序編譯時自動向程序插裝一些檢測指令,能夠做到對用戶屏蔽具體實現(xiàn),對被加固的程序也無特別限制,不需要操作系統(tǒng)的多線程支持。
(2)本發(fā)明對控制流錯誤的檢測率很高,能夠解決很多傳統(tǒng)軟件實現(xiàn)的控制流檢測方法的檢測盲點。例如偽分支問題,由于本發(fā)明實現(xiàn)了分支預測機制,在執(zhí)行原有分支指令之前,先執(zhí)行同樣的指令進行分支判斷,相當于對分支指令進行了冗余計算,所以能夠過解決控制流指令本身出錯的問題。故障注入實驗的結果表明,本發(fā)明的控制流錯誤檢測率在99.2%以上。
(3)本發(fā)明能夠有效解決基本塊內部的控制流檢測問題,而且檢測效率很高。假設基本塊中原有指令n條,加入了m條的內部檢測指令,如果是按指令數(shù)平均劃分區(qū)域,那么被漏檢的可能向前跳轉的內部控制流錯誤有(m+1)×((n/(m+1))!)個。假設所有可能的前向內部控制流錯誤的概率相同,那么對于前向內部控制流錯誤的檢測率可以達到1-(m+1)×((n/(m+1))!)/((n+m)!)。設n=30,m=1,代入計算得可能漏檢的錯誤僅占3.18×10-22。所以雖然只加入了很少的內部檢測指令,但是對于基本內部控制流的檢測率非常高。本發(fā)明對于內部控制流錯誤檢測的優(yōu)勢還在于可以根據(jù)具體需求進行配置,用戶可以根據(jù)可靠性、性能的需求及程序運行環(huán)境自定義Ω值,通過設置Ω來決定實施塊內部控制流檢測的力度,而對于不需要內部控制流檢測機制的基本塊來說則沒有額外開銷。相比已有方法來說,本發(fā)明在成本和靈活性方面要好很多。當然如果基本塊內部控制流錯誤恰好發(fā)生在4.3步所劃分區(qū)域的內部(這種情況發(fā)生概率非常小),那么仍然不能檢測這種控制流錯誤。
(4)本發(fā)明能夠有效解決過程間控制流檢測的難點問題。由于在調用函數(shù)時通過RAR將返回目的基本塊標簽傳遞給了調用函數(shù),而且RAR中值存放在被調用函數(shù)的棧區(qū)間,所以本發(fā)明可以很好地處理嵌套調用和遞歸調用等復雜情況。
(5)本發(fā)明對程序本身性能影響很小,除內部的控制流檢測指令外,單個基本塊最多插入5條指令(對應于函數(shù)入口基本塊同時有兩個合法后繼基本塊的特殊情況),最少只插入2條指令。已有性能分析實驗的結果表明,在使用本發(fā)明的控制流檢測后,程序的性能開銷僅為15%~37%。
圖1是基本塊標簽的格式; 圖2是本發(fā)明的總流程圖; 圖3是本發(fā)明的第二步標識程序的基本塊并確定基本塊之間的路由關系的流程圖; 圖4是本發(fā)明的第三步基本塊標簽格式設計和靜態(tài)標簽分配的流程圖; 圖5是本發(fā)明的第四步向被檢測程序加入控制流檢測指令的流程圖。
具體實施例方式 圖1是基本塊標簽格式的示意圖。
基本塊標簽由基本標簽編碼和用于基本塊內部控制流檢測的編碼兩部分組成。每個基本塊的基本標簽編碼是對基本塊在程序總基本塊列表中的序號進行的編碼,是唯一的;而基本塊內部控制流檢測的編碼則是該塊所需的內部控制流檢測指令計數(shù)的二進制編碼,占整個基本塊標簽的最末幾位。
圖2是本發(fā)明的總流程圖。包括以下步驟 第一步,首先通過編譯器將需要進行控制流檢測的程序編譯成匯編代碼。
第二步,基于程序匯編代碼標識出程序的基本塊并確定基本塊之間的路由關系,即程序的控制流結構。
第三步,根據(jù)用戶對基本塊內部控制流檢測的需求和基本塊的構成特征,設計基本塊標簽,并為每個基本塊分配唯一的靜態(tài)標簽。
第四步,以函數(shù)為單位依次在程序每個基本塊的頭部、內部和尾部分別插入控制流檢測指令。
第五步,通過編譯器,將插入控制流檢測指令的匯編程序重新匯編并鏈接,生成可執(zhí)行的實現(xiàn)控制流檢測的程序。
圖3是本發(fā)明的第二步標識程序的基本塊并確定基本塊之間的路由關系的流程圖,主要包括三大步驟 1.依次遍歷匯編程序的指令序列,根據(jù)其中的程序控制指令標注基本塊的入口指令。同時根據(jù)指令的操作數(shù)字段識別出程序中所有使用過的寄存器,最后得出哪些寄存器還沒有被程序使用過。
2.重新依次遍歷匯編程序的指令序列,將一個被標注為基本塊入口指令到下一個入口指令之間的指令劃分為一個基本塊。
3.依次遍歷程序的所有基本塊,根據(jù)基本塊的最后一條指令確定基本塊之間的路由關系。
圖4是本發(fā)明的第三步基本塊標簽格式設計和靜態(tài)標簽分配的流程圖,該過程主要包括四個步驟 1.依次遍歷程序中所有基本塊,根據(jù)基本塊包含的指令的條數(shù)對閾值Ω的比例,計算在該基本塊的內部檢測指令計數(shù)(即需要多少條內部控制流檢測指令),得到整個程序中所有基本塊最大的內部檢測指令計數(shù)。
2.根據(jù)整個程序中所有基本塊最大的內部檢測指令計數(shù),設計基本塊標簽。
3.依次為每個基本塊分配唯一的靜態(tài)標簽AS。在所有靜態(tài)標簽中,用于基本塊內部控制流檢測的幾位編碼都保持為0。
4.從沒有被程序使用的空閑寄存器中選擇四個,分別指派給DSR、ASR、SVR和RAR。這四個寄存器分別用于保存程序運行過程中產生的動態(tài)標簽DS,基本塊的靜態(tài)標簽AS,標簽差異值SV和函數(shù)調用返回地址RA。
圖5是本發(fā)明的第四步插入控制檢測指令的流程圖,表示分別向每個基本塊頭部、內部和尾部插入指令,該過程主要包括三大步驟 1.根據(jù)基本塊的類型在基本塊頭部插入指令如果基本塊是函數(shù)入口基本塊,則先插入指令“store RAR,mem”,“mem”是該函數(shù)的空閑棧區(qū)間中的存儲單元;然后對于所有基本塊,需要插入指令“xor DSR=DSR,SVR”。
2.根據(jù)基本塊包含的指令數(shù)在基本塊內部插入內部控制流檢測指令按照用戶定義的基本塊內部檢測指令的間隔指令閾值Ω,依次每隔Ω大小的指令數(shù)就插入內部檢測指令“sub DSR=DSR,1”,直到剩下的指令總數(shù)不大于Ω。
3.根據(jù)基本塊的類型及其后繼基本塊,在基本塊的尾部插入指令。
1)如果當前基本塊不是函數(shù)調用基本塊和函數(shù)退出基本塊,而且只有一個后繼基本塊,則插入如下指令 xor SVR=DSR,ASnext 其中ASnext表示后繼基本塊的靜態(tài)標簽。如后繼基本塊需要進行塊內的控制流檢測,則ASnext等于后繼基本塊的靜態(tài)標簽加上相應的塊內部控制流檢測指令計數(shù)。
2)如果當前基本塊不是函數(shù)調用基本塊和函數(shù)返回基本塊,而且有兩個后繼基本塊(即基本塊最后一條指令是分支指令),則依次插入如下代碼 xor SVR=DSR,ASthen br L1′,brcond xor SVR=DSR,ASelse L1′ 新插入的3條指令實現(xiàn)了分支預測功能,其中ASthen和ASelse分別表示分支條件滿足的后繼塊和分支條件不滿足的后繼塊的靜態(tài)標簽。且分支預測指令“br L1′,brcond”的指令操作碼和分支條件“brcond”與實際的分支指令相同,但是分支的目標改成L1′,而且L1′必須是一個沒有在程序中出現(xiàn)過的語句標示符。同樣,如果后繼基本塊需要進行內部控制流檢測,則ASthen或ASelse分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù)。
3)如果當前基本塊是函數(shù)調用基本塊,則依次加入如下指令 xor SVR=DSR,AScallee mov RAR=ASreturn 其中,“xor SVR=DSR,AScallee”表示把DSR與被調用函數(shù)的入口基本塊的靜態(tài)標簽AScallee異或運算求標簽差異值,指令“mov RAR=ASreturn”表示把函數(shù)返回的目的基本塊的靜態(tài)標簽ASreturn賦予寄存器RAR。同樣,如果被調用函數(shù)的入口基本塊和把函數(shù)返回的目的基本塊需要進行塊內部的控制流檢測,則AScallee或ASreturn分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù)。
4)如果當前基本塊是函數(shù)退出基本塊,則依次加入如下指令 load RAR,mem xor SVR=DSR,RAR clear mem 其中,指令“l(fā)oad RAR,mem”表示從保存函數(shù)返回目的基本塊靜態(tài)標簽的內存區(qū)域mem中讀出數(shù)據(jù),寫回到寄存器RAR中。然后由指令“xor SVR=DSR,RAR”將DSR與RAR進行異或運算求標簽差異值?!癱lear mem”表示需要將使用的內存區(qū)域中的數(shù)據(jù)清除。
對于所有類型的基本塊,在尾部最后插入指令 br faultDet DSR?。紸Scurrent 表示把當前塊的靜態(tài)標簽AScurrent與DSR進行比較。如果不相等,則意味著檢測到控制流錯誤,需要轉向錯誤處理例程。
當指令集中沒有可以把寄存器和立即數(shù)直接進行比較的指令,則要先插入指令 mov ASR=AScurrent 表示先把AScurrent讀取到寄存器ASR中,然后插入 br faultDet DSR?。紸SR 表示將ASR與DSR進行比較。
權利要求
1.一種程序控制流錯誤檢測方法,其特征在于包括以下步驟
第一步,通過編譯器將需要進行控制流錯誤檢測的程序編譯成匯編代碼;
第二步,基于程序匯編代碼標識出程序的基本塊并確定基本塊之間的路由關系,具體分為三個步驟
2.1依次遍歷程序匯編代碼的指令序列,根據(jù)指令操作碼字段識別出程序控制指令,然后基于程序控制指令標注基本塊的入口指令,方法為所有函數(shù)的第一條指令標注為基本塊的入口指令;對于條件分支指令和無條件跳轉指令,分支或跳轉的目標指令標注為基本塊的入口指令;所有程序控制指令的后繼指令標注為基本塊的入口指令;在標注入口指令的同時,還根據(jù)指令的操作數(shù)字段識別出程序中所有使用過的寄存器,得出哪些寄存器還沒有被程序使用過;
2.2重新依次遍歷匯編程序的指令序列,將一個被標注為基本塊入口指令到下一個入口指令之間的指令劃分為一個基本塊;如果基本塊的入口指令有標識符,就以標識符作為基本塊的名稱;基本塊以函數(shù)為單位按照在程序代碼中出現(xiàn)的順序列表組織,形成函數(shù)基本塊列表,且在函數(shù)基本塊列表中,第一個基本塊標記為函數(shù)入口基本塊,最后一個基本塊被標記為函數(shù)退出基本塊;所有函數(shù)基本塊列表構成程序總的基本塊列表;
2.3依次遍歷匯編程序的所有基本塊,按照基本塊的最后一條指令確定基本塊之間的路由關系,具體方法如下如果基本塊最后一條指令是條件分支指令和無條件跳轉指令,則根據(jù)指令的分支或跳轉目標地址查找對應的目標基本塊,在程序對應的控制流圖中從當前塊向該目標基本塊劃一條有向邊;如果基本塊的最后一條指令是條件分支指令或普通指令——即不是無條件跳轉指令、函數(shù)調用指令和函數(shù)返回指令,則在控制流圖中從當前塊向其直接后繼基本塊劃一條有向邊;如果基本塊最后一條指令是函數(shù)調用指令,則在控制流圖中從當前塊向被調用函數(shù)的入口基本塊劃一條有向邊,并且從被調用函數(shù)的退出基本塊向當前塊在所屬函數(shù)基本塊列表中的下一個塊劃一條有向邊,表示函數(shù)返回的控制流轉移;
第三步,根據(jù)用戶對基本塊內部控制流錯誤檢測的需求和基本塊的構成特征,設計基本塊標簽,并為每個基本塊分配唯一的靜態(tài)標簽,采用可配置的方法進行內部控制流校驗,即當基本塊內部的指令計數(shù)每超過閾值Ω時才進行一次內部控制流校驗,Ω由用戶根據(jù)可靠性、性能的需求及程序運行環(huán)境自定義,具體步驟為
3.1依次遍歷程序中所有基本塊,根據(jù)基本塊包含指令的條數(shù)對閾值Ω的比例∑,計算在該基本塊的內部檢測指令計數(shù),該內部檢測指令計數(shù)等于
再根據(jù)程序中每個基本塊的內部檢測指令計數(shù)得到整個程序中所有基本塊最大的內部檢測指令計數(shù);
3.2根據(jù)整個程序中所有基本塊最大的內部檢測指令計數(shù),設計基本塊標簽,基本塊標簽由基本標簽編碼和用于基本塊內部控制流檢測的編碼兩部分組成,每個基本塊的基本標簽編碼是該基本塊在程序總基本塊列表中的序列號的二進制編碼,是唯一的,基本塊內部控制流檢測的編碼是該塊所需的內部控制流檢測指令計數(shù)的二進制編碼,占整個基本塊標簽的最末幾位;在基本塊標簽的末端為塊內部控制流檢測分配
位編碼,N為3.1步分析得出的整個程序中一個基本塊中最多需要的內部檢測指令條數(shù);
3.3依次為每個基本塊分配唯一的靜態(tài)標簽,所有靜態(tài)標簽中用于基本塊內部控制流檢測的編碼都保持為0;
3.4從步驟2.1確定的空閑寄存器中選擇四個寄存器分別定義為DSR、ASR、SVR和RAR,分別保存程序運行過程中產生的動態(tài)標簽DS、靜態(tài)標簽AS、標簽差異值SV和函數(shù)調用返回地址RA;其中,SV是兩個基本塊的靜態(tài)標簽異或運算的結果;如果程序沒有四個空閑寄存器可供使用,則通知用戶無法實現(xiàn)控制流錯誤檢測,用戶選擇采用編譯器的其它寄存器分派策略以重新生成匯編程序;
第四步,依次在程序每個基本塊的頭部、內部和尾部分別插入相應指令,具體步驟包括
4.1進入一個函數(shù)后,首先針對函數(shù)入口基本塊Bentry和函數(shù)退出基本塊Bexit進行特殊處理在調用函數(shù)的基本塊Bcaller把返回的目的基本塊Breturn的靜態(tài)標簽通過寄存器RAR傳遞給被調用函數(shù),被調用函數(shù)的Bentry塊把RAR中的數(shù)據(jù)寫到分配給本函數(shù)的棧區(qū)間,最后被調用函數(shù)的Bexit塊把保存的Breturn靜態(tài)標簽從棧區(qū)間重新讀出,實現(xiàn)對過程間控制流的跟蹤,所述Breturn是指在調用函數(shù)的基本塊列表中當前基本塊的下一個塊,具體包括三個步驟
4.1.1首先確定分配給本函數(shù)的棧區(qū)間中是否有空閑單元可用于存放RAR中的數(shù)據(jù),如果確實沒有空閑區(qū)域,則修改Bentry塊入口處用于分配棧區(qū)間的指令,使得該函數(shù)的棧區(qū)間有一個機器字長的空閑存儲單元可用于控制流檢測使用;然后從函數(shù)的??臻g的空閑區(qū)域中,選擇其中一個機器字長的存儲單元,記為mem;
4.1.2在Bentry塊分配棧區(qū)間的指令后加入指令“store RAR,mem”,表示把RAR中的數(shù)據(jù)寫到分配給本函數(shù)棧區(qū)間的mem單元之中;
4.1.3如果Bentry塊入口處修改了分配棧區(qū)間的指令,則相應修改Bexit塊入口處回收棧區(qū)間的指令,使得函數(shù)棧區(qū)間的分配和回收保持一致;
4.2在當前基本塊頭部插入指令“xor DSR=DSR,SVR”,表示將動態(tài)標簽與標簽差異值異或運算生成新的動態(tài)標簽DS,所述當前基本塊頭部是指基本塊第一條指令之前,對于函數(shù)入口基本塊來說則在4.1.2步驟的指令“store RAR,mem”之后;
4.3按照設定的Ω值,在基本塊內部插入將DSR減1的內部控制流檢測指令,方法是依次遍歷基本塊的指令序列,每隔Ω大小的指令數(shù)就插入一條內部控制流檢測指令“subDSR=DSR,1”,直到剩下的指令總數(shù)不大于Ω值;對于那些本身包含指令的數(shù)目小于Ω值的基本塊則不需要進行內部控制流檢測,基本塊內部保持不變;
4.4在基本塊的尾部按照基本塊的類型將DSR與后繼基本塊的靜態(tài)標簽進行異或運算,產生新的標簽差異值SV,所述基本塊的尾部是指基本塊最后一條指令之后,如果最后一條指令是程序控制指令,則在該程序控制指令之前;具體方法如下
4.4.1如果當前基本塊既不是函數(shù)調用基本塊又不是函數(shù)退出基本塊,而且只有一個后繼基本塊,則在當前基本塊的尾部插入指令“xor SVR=DSR,ASnext”,表示直接把后繼塊的靜態(tài)標簽值ASnext與DSR進行異或運算,求得新的標簽差異值后賦給SVR;如果后繼基本塊需要進行塊內控制流檢測,ASnext等于后繼基本塊靜態(tài)標簽加上相應的內部控制流檢測指令計數(shù);
4.4.2如果當前基本塊的最后一條指令是分支指令,則采取分支預測方法提前判斷將要執(zhí)行哪條分支,然后把所預測分支對應的后繼基本塊的靜態(tài)標簽與DSR進行異或運算,求得新的標簽差異值SV;具體方法是設ASthen和ASelse分別表示分支條件滿足和不滿足的目的基本塊靜態(tài)標簽,首先在當前基本塊的尾部插入指令“xor SVR=DSR,ASthen”,表示先假設分支條件滿足,將DSR與ASthen進行異或運算求SVR值;然后在“xor SVR=DSR,ASthen”之后插入分支預測指令“br L1′,brcond”,分支條件“brcond”與實際的分支指令相同,而分支的目標“L1′”必須是一個沒有在程序中出現(xiàn)過的語句標示符,具體指向4.5步即將插入的校驗DSR數(shù)據(jù)的指令;最后在“br L1′,brcond”之后插入指令“xor SVR=DSR,ASelse”,表示將DSR與分支條件不滿足的目的基本塊的靜態(tài)標簽進行異或運算求得SVR值;如果分支條件滿足,分支預測指令“br L1′,brcond”執(zhí)行結果將使得程序跳過指令“xor SVR=DSR,ASelse”,即SVR中的值是DSR與ASthen異或運算的執(zhí)行結果,否則SVR中的值是DSR與ASelse異或運算的運算結果;如果后繼基本塊需要進行內部控制流檢測,則ASthen或ASelse分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù);
4.4.3如果當前基本塊是函數(shù)調用基本塊,其后續(xù)執(zhí)行的基本塊是被調用函數(shù)的入口基本塊,先在當前基本塊的尾部插入指令“xor SVR=DSR,AScallee”,實現(xiàn)DSR與被調用函數(shù)入口基本塊靜態(tài)標簽AScallee的異或運算,并把標簽差異值賦予SVR;然后在“xor SVR=DSR,AScallee”之后插入指令“mov RAR=ASreturn”,表示把函數(shù)調用返回的目的基本塊的靜態(tài)標簽ASreturn賦予寄存器RAR;如果后續(xù)執(zhí)行的被調用函數(shù)的入口基本塊或者函數(shù)返回的目的基本塊需要進行基本塊內部的控制流檢測,則AScallee或ASreturn分別等于其靜態(tài)標簽加上對應的內部控制流檢測指令計數(shù);所述函數(shù)調用返回的目的基本塊是指當前基本塊在所屬函數(shù)的基本塊列表中的下一個塊;
4.4.4如果當前基本塊是函數(shù)退出基本塊,先在當前基本塊的尾部插入指令“l(fā)oad RAR,mem”,表示從mem中讀出數(shù)據(jù),寫回到寄存器RAR中;然后在“l(fā)oad RAR,mem”后插入指令“xor SVR=DSR,RAR”,表示將DSR與RAR進行異或運算求新標簽差異值;最后在“xor SVR=DSR,RAR”后插入指令“clear mem”,表示將使用的內存單元mem中的數(shù)據(jù)清除;
4.5在基本塊的尾部加入校驗DSR數(shù)據(jù)的指令“br faultDet,DSR?。紸Scurrent”,其中AScurrent表示分派給當前基本塊的靜態(tài)標簽;如果基本塊的最后一條指令是程序控制指令,校驗DSR數(shù)據(jù)的指令在程序控制指令之前插入;
第五步,通過編譯器,將插入了控制流檢測指令的匯編程序重新匯編并鏈接,生成可執(zhí)行的實現(xiàn)控制流錯誤檢測的程序。
全文摘要
本發(fā)明公開了一種程序控制流錯誤檢測方法,目的是克服已有方法提高控制流錯誤的檢測率,解決基本塊內部、過程間控制流檢測難點問題。技術方案是先標識基本塊并確定基本塊之間的路由關系;然后根據(jù)基本塊內部控制流檢測的需求和基本塊的構成特征,確定單個基本塊最多需要多少條內部控制流檢測指令,在此基礎上設計基本塊標簽,并為每個基本塊分配唯一的靜態(tài)標簽;然后在程序中的每個基本塊的頭部、內部和尾部分別插入檢測指令,將插入了控制流檢測指令的匯編程序重新匯編鏈接,生成可執(zhí)行的實現(xiàn)控制流錯誤檢測的程序。采用本發(fā)明能解決檢測盲點問題,檢測率很高;且能有效解決基本塊內部、過程間控制流檢測的難點問題。
文檔編號G06F9/45GK101763291SQ200910226768
公開日2010年6月30日 申請日期2009年12月30日 優(yōu)先權日2009年12月30日
發(fā)明者譚慶平, 徐建軍, 寧洪, 周會平, 李建立, 李劍明, 羅宇, 鄧勝蘭 申請人:中國人民解放軍國防科學技術大學