陳 暢 劉福來
(廣東電網(wǎng)有限責任公司廣州供電局 廣州 510620)
(change.c@163.com)
隨著社會與網(wǎng)絡技術的飛速發(fā)展,智能電網(wǎng)正在深度影響著每個人的生活,其發(fā)展正從發(fā)電、輸電、變電、配電、用電和調度各個環(huán)節(jié)改變著傳統(tǒng)的電網(wǎng)模式[1].建設高效、環(huán)保、安全的智能電網(wǎng)成為我國能源安全新戰(zhàn)略.然而想要實現(xiàn)這一目標,在我國現(xiàn)有條件背景的支持下,還需要提高針對性的安全加固手段,包括從芯片、操作系統(tǒng)、功能運用各層級系統(tǒng)化的安全評估體系.同時,利用芯片設計改變芯片運算邏輯的硬件木馬應運而生,它對電網(wǎng)系統(tǒng)的攻擊性是巨大的,因此要保證智能電網(wǎng)系統(tǒng)的穩(wěn)定運行,意味著對二次系統(tǒng)所管理資源的安全性、保密性、完整性提出了新的挑戰(zhàn).
1) 研究背景
經(jīng)歷了長時間的發(fā)展和市場的檢驗,二次系統(tǒng)的特點逐漸凸顯,首先要保證系統(tǒng)信息的機密性,只有授權人員才能通過計算機訪問系統(tǒng)資源,其次要保證數(shù)據(jù)信息是完整的,未經(jīng)授權不得更改.
控制指令被竊取,顧名思義就是主站命令參數(shù)設置無法安全鑒別和保持數(shù)據(jù)完整性,對變電站失去了控制,即使人員操作失誤也無法識別,易引發(fā)變電站事故,如果自動化裝置中的軟件配置未使用統(tǒng)一規(guī)范,或者設備廠家攜帶的U盤不安全,就會給變電站自動化裝置帶來很大隱患.控制指令被竊取的配置管理正確性依賴于廠家,即工程備份不可控.
內(nèi)存泄露的本質是某些位置的內(nèi)存無法控制或利用,對于軟件系統(tǒng)來說,偶爾發(fā)生1次內(nèi)存泄露并不會產(chǎn)生特別大的危害,用戶可能都不會察覺到,或者對于整個系統(tǒng)并沒有產(chǎn)生大的影響.真正的危害是內(nèi)存泄露的累積,因為內(nèi)存泄露會導致某些內(nèi)存單元里存放著無用的或者無意義的數(shù)據(jù),從而變得不可利用.隨著內(nèi)存泄露的堆積,不可利用的內(nèi)存單元越來越多,相對地系統(tǒng)可利用的內(nèi)存資源就越來越少,最終導致系統(tǒng)卡頓,甚至直接導致系統(tǒng)崩潰.
2) 相關工作
目前,變電站是專業(yè)密集、設備密集的重資產(chǎn)主體.國內(nèi)智能站通過10多年的試點建設,已經(jīng)研究出集采集信息、測量、計量等基本功能于一體的實時自動控制電網(wǎng).因此,智能電網(wǎng)具有空間維度上復雜的動態(tài)性、不確定性、非線性等特點[2].例如南方電網(wǎng),運用組網(wǎng)跳閘模式,使信息共享更為高效,已初具智能站技術規(guī)范體系.然而自動化安全裝置也存在著多種多樣的安全問題,主要有:不能完全保障安全性的計算機平臺安全機制、過于簡單的自動化裝置安全架構等.原因則更多樣化,主要有:系統(tǒng)本身存在漏洞、使用人員安全意識薄弱無法抵擋黑客入侵及硬件架構不完善等.目前,嵌入式系統(tǒng)的安全機制應用廣泛,國內(nèi)外采用的方法也不盡相同,國內(nèi)主要方法是使用防護、檢測、恢復機制對系統(tǒng)進行安全保護,分析硬件行為從而達到保證信息系統(tǒng)安全可靠的目的.
要想有效地減少內(nèi)存泄露,必須在充分了解內(nèi)存泄露發(fā)生原理的基礎上,對內(nèi)存泄露進行準確地檢測,從而排除內(nèi)存泄露的隱患.目前相關領域研究主要集中在靜態(tài)檢測、動態(tài)檢測以及靜態(tài)和動態(tài)檢測結合這3大方向.靜態(tài)檢測的關鍵是程序的控制流圖,并以內(nèi)存泄露的原理為基礎設計算法,對程序控制流圖進行檢測[3].動態(tài)檢測則是在程序運行的同時進行內(nèi)存泄露的檢測,相較于靜態(tài)檢測方法,動態(tài)檢測程序運行過程中實時檢測,準確率較高;缺點是需要執(zhí)行目標代碼,這就導致了對測試用例的依賴,還會導致部分代碼未被執(zhí)行到,運行開銷可能會很大[4].
3) 本文主要成果
基于研究背景,本文結合C++的語言特點論述了電網(wǎng)特點以及內(nèi)存泄露發(fā)生的原因,將研究過程分為預處理、中間模型構建以及檢測分析3個部分,結合示例代碼分別對各個部分進行詳細的方法設計說明,展示了基于數(shù)據(jù)流分析的安全漏洞檢測方法的基本思路.最后基于研究的內(nèi)存檢測方法,進行多組實驗,覆蓋全部漏洞的實際情況,對研究方法進行測試,并分析實驗結果.
在理解、分析內(nèi)存泄露之前,首先應該了解C++對于內(nèi)存如何進行管理.C++之所以具有靈活、高性能的特點,這與其靈活的內(nèi)存管理機制是分不開的.且C++將系統(tǒng)分配的內(nèi)存分為5個區(qū):棧、堆、自由存儲區(qū)、全局靜態(tài)存儲區(qū)、常量存儲區(qū)[5].
內(nèi)存泄露作為一種常見的程序缺陷,因其特性經(jīng)常不被設計人員重視,如果不加以重視,內(nèi)存泄露會造成嚴重的后果,輕則造成電網(wǎng)程序卡頓,重則導致整個二次系統(tǒng)的崩潰.
內(nèi)存泄露的具體概念是:在程序運行過程中,已經(jīng)動態(tài)分配的內(nèi)存單元,由于錯誤釋放或未釋放等原因,導致這部分內(nèi)存不能再次使用,從而影響了電網(wǎng)程序的運行,嚴重的可能會導致系統(tǒng)崩潰.內(nèi)存泄露具有隱蔽性、累積性,且在某些情況下具有偶發(fā)性.
1.1.1 產(chǎn)生原因
C/C++作為允許顯式動態(tài)內(nèi)存分配的語言,程序設計人員可以手動分配智能電網(wǎng)系統(tǒng)內(nèi)存,因此具備了高效性、靈活性,但是隨之而來的隱患就是內(nèi)存泄露.
根據(jù)發(fā)生內(nèi)存泄露的場景進行分類,內(nèi)存泄露歸納為以下幾種情況[6]:
1) 使用new和delete語句分別對類的對象構造函數(shù)和析構函數(shù)進行調用,實現(xiàn)對象的創(chuàng)建和銷毀,但是由于開發(fā)人員的失誤,new和delete語句沒有相應進行匹配;
2) 對于嵌套的對象指針的不正確操作,導致指針混亂;
3) 通過delete語句釋放對象數(shù)組時錯誤地使用,或者沒有使用方括號[];
4) 混淆對象數(shù)組與指向對象的指針數(shù)組;
5) 復制構造函數(shù)的缺失.
1.1.2 分類
如果根據(jù)發(fā)生的形式和頻率分類,內(nèi)存泄露可以分為常發(fā)性內(nèi)存泄露、偶發(fā)性內(nèi)存泄露、一次性內(nèi)存泄露、隱式內(nèi)存泄露4類.
數(shù)據(jù)流分析通常用于程序沒有運行時靜態(tài)分析源代碼,以預測程序動態(tài)運行時的過程.數(shù)據(jù)流分析并不是真正的運行程序,而是在運行之前進行靜態(tài)地分析,預測程序真正執(zhí)行時的相關信息.數(shù)據(jù)流分析的基礎是程序控制流圖,它將程序所有可能的執(zhí)行路徑記錄下來,程序實際運行時執(zhí)行的路徑會根據(jù)賦值變化.
本文提出一種基于數(shù)據(jù)流分析的內(nèi)存泄露檢測,該方法包括3部分:預處理部分、數(shù)據(jù)存儲部分以及檢測部分,如圖1所示:
圖1 方法總體設計
預處理,就是需要對內(nèi)存泄露檢測的目標代碼進行預處理,以達到為后續(xù)數(shù)據(jù)存儲部分作鋪墊的目的.為了能夠通過數(shù)據(jù)流分析檢測目標代碼的內(nèi)存泄露,對目標代碼進行合適的預處理是必不可少的環(huán)節(jié).如圖2所示,在本文研究中,對目標代碼的預處理主要包括詞法、語法分析,提取抽象語法樹以及生成程序控制流圖幾個部分.
圖2 預處理階段
2.1.1 提取抽象語法樹
抽象語法樹將源代碼語法結構通過抽象樹的形式表示出來,常作為中間表示進行分析.其中,樹中的每一個節(jié)點代表源代碼的一個語法結構.同時它也是程序源代碼的一種中間結構,需要進行語法分析,且作為連接語法分析和后端的接口,能夠表示語法本身的自然結構,對后端的操作不產(chǎn)生影響[7].
2.1.2 生成控制流圖
控制流圖是一種重要的數(shù)據(jù)結構,主要進行程序分析和源代碼結構分析,可以由源代碼的抽象語法樹得到.數(shù)據(jù)在程序中流動需要有依附,數(shù)據(jù)流依附程序的控制流,即數(shù)據(jù)的流動沿著控制流圖中程序的執(zhí)行路徑流動[8].
將代碼語句抽象為樹結構,得到程序流程圖,如圖3(a)所示.流程圖清晰反映程序運行的具體步驟,然后經(jīng)過對流程圖簡化得到更加突出控制流結構的控制流圖,如圖3(b)所示.
圖3 程序流程圖和控制流圖
目標代碼經(jīng)過語法分析得到抽象語法樹,畫出程序控制流圖,通過自定義的程序控制流圖,可以實現(xiàn)對需要的信息(關于內(nèi)存操作的語句信息)的提取.但是想要進一步地設計算法對這些進行處理,達到電網(wǎng)內(nèi)存泄露檢測的目的,控制流圖的信息并不能被直接利用.本文研究中,在能夠完整地存儲每個節(jié)點提取出來的信息的前提下,還要體現(xiàn)節(jié)點之間的關系,并且方便解析、讀取,因此選擇XML(extensible markup language)作為中間模型的構建基礎.
XML是一種可拓展的標志性語言,關鍵在于能對每一個數(shù)據(jù)打上標簽,以描述、標記不同的數(shù)據(jù).同一種標簽包括開始標簽和結束標簽,中間是該標簽定義的數(shù)據(jù),同時在標簽中又可以嵌套1個或多個標簽,這樣就可以體現(xiàn)出數(shù)據(jù)之間的關系.
本文選擇利用TinyXML開源庫進行XML中間模型的解析,其主要原因是TinyXML的基礎是文檔對象模型,這種樹結構在易于理解的同時,也為用戶提供了用于訪問的面向對象接口[9].XML中間模型解析(寫入和讀取)的基礎是多個面向對象的訪問接口,訪問XML文檔信息時使用分層對象模型,這些分層對象模型根據(jù)XML的文檔結構形成1棵節(jié)點樹,如圖4所示.其中,各節(jié)點表示的訪問接口可歸納如下:
圖4 TinyXML
1) TiXmlDocument.文檔類,代表了整個XML文件.
2) TiXmlDeclaration.聲明類,表示文件的聲明部分.
3) TiXmlComment.注釋類,表示文件的注釋部分.
4) TiXmlElement.元素類,是文件的主要部分,支持嵌套結構,一般使用這種結構對存儲信息進行分類,可以包含屬性類和文本類.
5) TiXmlAttribute/TiXmlAttributeSet.元素屬性,一般嵌套在元素中,記錄該元素的一些屬性.
6) TiXmlText.文本對象,嵌套在某個元素內(nèi)部.
智能電網(wǎng)系統(tǒng)程序中比較常見的問題是堆內(nèi)存泄露,malloc,new等操作符用于程序分配堆內(nèi)存,使用之后必須調用free或delete顯式釋放分配的堆內(nèi)存.如果由于程序設計的疏忽導致這部分堆內(nèi)存沒有釋放或者錯誤釋放,使這部分內(nèi)存無法再次使用,就會造成內(nèi)存泄露[10].
在本文研究中,基于數(shù)據(jù)流分析的內(nèi)存泄露故障檢測思路如下:
1) 根據(jù)目標代碼中與內(nèi)存操作相關語句的控制流圖,生成形式化定義的控制流圖節(jié)點組成的所有可達路徑.
2) 根據(jù)已經(jīng)生成的可達路徑,構建每一條路徑對應的XML中間模型,存儲路徑上關于內(nèi)存操作的可用信息,方便后續(xù)模擬內(nèi)存操作.
3) 通過哈希表模擬路徑中每一個節(jié)點對內(nèi)存的操作,例如分配、釋放堆內(nèi)存.
4) 在一條路徑上所有的節(jié)點對內(nèi)存操作全部進行之后,通過判斷哈希表的狀態(tài),推斷目標代碼是否存在內(nèi)存泄露.
操作過程如表1所示:
表1 內(nèi)存操作過程
C++中常見的內(nèi)存操作有增(add)、刪(delete)、查(get)、改(modify),這也可以對應于哈希表中的操作,可以使用哈希表的操作模擬C++程序中內(nèi)存的操作,處理過程如表2所示:
表2 哈希表處理過程
本文開展的實驗所用設備是個人電腦,如表3所示.其配置如下:操作系統(tǒng)使用Windows10家庭中文版,處理器由12個Intel?CoreTMCPU 組成,型號為i7-9750H,內(nèi)存大小為8 GB,顯卡型號為NVIDIA GeForce GTX 1650,顯卡內(nèi)存為4 GB.
表3 實驗設備
本實驗采用的開發(fā)平臺是Visual Studio 2019,使用的編程語言是C++.通過在Visual Studio上安裝C++的桌面開發(fā)工作負載,構建本次實驗的基本開發(fā)環(huán)境.除此之外,本實驗使用了大量的C++第三方庫以及開源工具,主要有TinyXML和cppcheck.
本文研究的內(nèi)容是基于數(shù)據(jù)流分析的內(nèi)存泄露檢測方法,這種方法可以直接得到檢測結果,無需程序運行,但該方法的缺點是存在誤報的風險.本文研究的實驗驗證分為3個階段:
1) 詞法分析、語法分析簡單實現(xiàn);
2) 實現(xiàn)對XML中間模型的解析,驗證其可用性;
3) 測試該方法對內(nèi)存泄露檢測的可靠性.
3.3.1 詞法分析及語法分析簡單實現(xiàn)
抽象語法樹的生成主要分為3個部分:詞法分析、語法分析以及樹的生成.本文基于C++程序的特點,分別實現(xiàn)了簡易的詞法分析器和語法分析器.
首先編制一個讀單詞過程[11],從輸入的源程序中,識別出各個具有獨立意義的單詞,如算法1所示.即基本保留字、標識符、常數(shù)、運算符、分隔符5大類,并依次輸出各個單詞的內(nèi)部編碼及單詞符號自身值,如表4所示:
表4 詞法分析結果
算法1.詞法分析.
輸入:inti,ans; charc; scanc; doubleb=1.5; floatf.
for (i=0;i<5;i=i+1)
ans=ans+1;
end for
輸出:printans; return 0.
語法分析時,使用遞歸下降分析法對算術表達式(包括+,-,*,/,())進行分析,一次性來判斷該表達式正確與否,得到簡單語法分析結果[12].如表5所示:
表5 語法分析結果
3.3.2 XML中間模型解析
XML中間模型解析原理基于開源的TinyXML庫,利用文檔對象一次性將XML文件導入到內(nèi)存中,根據(jù)文檔內(nèi)容建立樹結構,實現(xiàn)存儲的功能.同時通過分析利用TinyXML庫,按照本文預先定義的節(jié)點形式實現(xiàn)XML中間模型的解析,生成控制流圖節(jié)點XML模型表示結果,如圖5所示:
圖5 XML模型解析結果
3.3.3 內(nèi)存泄露檢測方法驗證
本文實驗借助開源工具cppcheck部分模塊,實現(xiàn)了基于數(shù)據(jù)流分析的內(nèi)存泄露檢測方法,測試用例程序如圖6所示:
圖6 待測目標代碼
除此之外,結合實際程序開發(fā)過程的需求,將內(nèi)存泄露工具與visual studio的使用結合起來,通過外部工具的方式實現(xiàn)在visual studio中直接調用該工具進行內(nèi)存泄露的檢測[10].
該程序第15行處存在內(nèi)存泄露.實驗證明在不運行程序的情況下,該工具能夠準確地檢測出內(nèi)存泄露的風險,并且顯示出存在內(nèi)存泄露的代碼行數(shù),如圖7所示:
圖7 內(nèi)存泄露檢測結果
本文提出一種基于電網(wǎng)操作系統(tǒng)中數(shù)據(jù)流分析的內(nèi)存泄露檢測方法,在廣泛查閱資料和借助開源工具的基礎上自主實現(xiàn)了目標代碼的詞法分析、語法分析.設計了一種規(guī)范的自定義XML中間模型用于存儲相關信息.同時基于特定算法實現(xiàn)內(nèi)存泄露靜態(tài)檢測.實驗表明,該方法能有效檢測內(nèi)存泄露問題,在自動化測試、程序靜態(tài)檢測等方面具有實際意義[13].
如今電網(wǎng)程序的并行化程度和復雜性逐漸提高,基于電網(wǎng)系統(tǒng)中數(shù)據(jù)流分析的內(nèi)存泄露檢測方法在準確性方面還存在一定的限制.因此,如何進一步提高檢測效率和可靠性是后續(xù)研究的目標.另外,對于內(nèi)存泄露的檢測,使用靜態(tài)檢測和動態(tài)檢測結合的思想[14]是通過運行之前的靜態(tài)檢測找出內(nèi)存泄露風險點,然后在運行的同時對風險點進行進一步檢查.動態(tài)與靜態(tài)結合檢測既可以減少檢測對于程序運行的影響,又對檢測的可靠性和準確率提供保證,這也是未來對于內(nèi)存泄露檢測的研究趨勢.