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