楊 斌,蔣 維,常澤海
(1.西安交通大學(xué)核科學(xué)與技術(shù)學(xué)院,西安 710049;2.中國核動(dòng)力研究設(shè)計(jì)院核反應(yīng)堆系統(tǒng)設(shè)計(jì)技術(shù)重點(diǎn)實(shí)驗(yàn)室,成都 610213)
隨著核電領(lǐng)域的自動(dòng)化和數(shù)字化發(fā)展,核電儀控軟件在整個(gè)核電系統(tǒng)中扮演的角色越來越重要,這對(duì)核電儀控軟件提出了更高的安全性和可靠性要求。由于C語言的高性能且能夠直接操作硬件的特性,目前大部分核電儀控軟件采用C語言進(jìn)行開發(fā)。然而,C語言的不安全特性使得開發(fā)的軟件系統(tǒng)易出現(xiàn)安全性問題,核電安全級(jí)儀控軟件開發(fā)過程中必須通過遵循嚴(yán)格的安全標(biāo)準(zhǔn)規(guī)范以及引入一系列軟件開發(fā)活動(dòng)以規(guī)避C語言的安全風(fēng)險(xiǎn)。目前常用的標(biāo)準(zhǔn)有:MSRA-2004、GJB-5369-2005《航天型號(hào)軟件C語言安全子集》、GB/T28169-2011《嵌入式軟件C語言編碼規(guī)范》[1]等,軟件開發(fā)活動(dòng)包括軟件代碼靜態(tài)分析、軟件測試和軟件V&V活動(dòng)[2]。一方面,檢測工具的檢測能力和準(zhǔn)確性直接影響檢測結(jié)果,代碼檢測工具存在誤報(bào)或漏檢的可能;另一方面,無論是軟件測試還是軟件V&V,均無法保證百分之百的覆蓋率。因此,引入標(biāo)準(zhǔn)規(guī)范和軟件開發(fā)活動(dòng)屬于被動(dòng)防御手段,并不能完全規(guī)避C語言的不安全特性帶來的安全風(fēng)險(xiǎn)。
Rust語言自問世以來,致力于成為優(yōu)雅解決高并發(fā)、高安全系統(tǒng)問題的編程語言,適用于大型場景,創(chuàng)造維護(hù)能夠保持大型系統(tǒng)完整的邊界,因此更加強(qiáng)調(diào)安全性、內(nèi)存布局控制和并發(fā)處理方面的特性,在網(wǎng)絡(luò)服務(wù)器領(lǐng)域引起極大重視。核電領(lǐng)域因其特殊性,Rust語言應(yīng)用尚處于探索階段。本文就Rust語言應(yīng)用于核電安全級(jí)儀控軟件開發(fā),以規(guī)避C語言的安全風(fēng)險(xiǎn)的可能性進(jìn)行探討。
C語言是一種不安全的語言,其固有特性易使得所開發(fā)的程序存在安全缺陷,同時(shí)其極大的靈活性也容易導(dǎo)致開發(fā)人員的不安全使用行為,均為軟件帶來了安全隱患,可能導(dǎo)致軟件失效。
在C語言中未對(duì)指針的使用加以限制,指針理論上可以訪問任意的內(nèi)存空間。若訪問了非法或者受保護(hù)的內(nèi)存地址,則會(huì)導(dǎo)致內(nèi)存問題,然而C語言編譯器并未對(duì)指針操作進(jìn)行嚴(yán)格的安全性檢查。這帶來了如下幾方面的問題:
1)C語言不對(duì)空指針進(jìn)行檢查,空指針?biāo)赶虻膬?nèi)存空間一般是受保護(hù)的。在軟件中對(duì)空指針進(jìn)行解引用,會(huì)引發(fā)軟件異常。
2)C語言中不強(qiáng)制要求指針初始化,未初始化的指針即是野指針。野指針的值取決于以前遺留的是什么值,因此它可能指向未知的內(nèi)存空間,對(duì)野指針進(jìn)行解引用,可能會(huì)引發(fā)軟件異常,也可能不會(huì)。但無論如何,這個(gè)行為不屬于預(yù)期內(nèi)的安全行為,存在安全隱患。
3)C語言使用指針訪問動(dòng)態(tài)申請(qǐng)的內(nèi)存空間,當(dāng)內(nèi)存空間使用完畢并進(jìn)行釋放后,指針仍舊指向已釋放的空間,此時(shí)的指針便是懸空指針。它與野指針類似,同樣會(huì)操作不屬于自己的內(nèi)存空間,導(dǎo)致安全問題。圖1展示了對(duì)懸空指針進(jìn)行操作的情況。
C語言內(nèi)存管理由開發(fā)人員通過內(nèi)存管理函數(shù)手動(dòng)進(jìn)行分配和釋放,C語言提供了一系列的內(nèi)存管理函數(shù)(如malloc、calloc、recalloc、free等)。若使用不當(dāng),會(huì)引發(fā)內(nèi)存問題。然而,C編譯器在編譯階段并不能暴露此種情況,導(dǎo)致運(yùn)行過程中出現(xiàn)內(nèi)存錯(cuò)誤,可能引發(fā)軟件崩潰。
常見的內(nèi)存問題有內(nèi)存泄漏和內(nèi)存釋放錯(cuò)誤。動(dòng)態(tài)申請(qǐng)的內(nèi)存使用完畢后,并未調(diào)用相應(yīng)的釋放函數(shù)對(duì)其進(jìn)行釋放,則會(huì)造成內(nèi)存泄漏[3]。C語言無法暴露內(nèi)存泄漏問題,內(nèi)存泄漏導(dǎo)致可用的內(nèi)存越來越少,最終引發(fā)程序崩潰,圖2對(duì)此種情況進(jìn)行了展示。內(nèi)存的分配和釋放要配對(duì),若對(duì)一塊內(nèi)存空間執(zhí)行重復(fù)釋放,也會(huì)造成內(nèi)存釋放錯(cuò)誤。若指針指向的地址并不是內(nèi)存分配后的地址,對(duì)其執(zhí)行釋放操作,也是危險(xiǎn)的。
圖2 內(nèi)存泄漏Fig.2 Memory leak
2004年以來,微軟安全響應(yīng)中心(MSRC)已對(duì)所有報(bào)告過的微軟安全漏洞進(jìn)行了分類。根據(jù)他們提供的數(shù)據(jù),所有微軟每年的補(bǔ)丁中約有70%是針對(duì)內(nèi)存安全漏洞的修復(fù)程序,故而微軟正在考慮使用新的內(nèi)存安全語言替代C/C++語言進(jìn)行操作系統(tǒng)的開發(fā)。
Rust語言是Mozilla主導(dǎo)開發(fā)的通用、編譯型語言,專注于安全,尤其是并發(fā)安全[4]。設(shè)計(jì)準(zhǔn)則為“安全、并發(fā)、實(shí)用”,支持函數(shù)式、并發(fā)式、過程式以及面向?qū)ο蟮木幊田L(fēng)格。在語法上和C++類似,但是在設(shè)計(jì)上保證性能的同時(shí)需提供更好的內(nèi)存安全,通過一系列的手段讓不安全的內(nèi)存風(fēng)險(xiǎn)在編譯階段暴露,保證了程序運(yùn)行中的內(nèi)存安全。
每種編程語言都有其管理使用計(jì)算機(jī)內(nèi)存的方式,要么通過開發(fā)人員手動(dòng)進(jìn)行內(nèi)存的分配和釋放(如C語言),要么提供垃圾回收機(jī)制,在程序運(yùn)行時(shí)不斷尋找不再被使用的內(nèi)存并進(jìn)行釋放(如Java語言)。前者將內(nèi)存管理權(quán)交給了開發(fā)人員,存在人因的內(nèi)存安全風(fēng)險(xiǎn),后者會(huì)降低程序運(yùn)行時(shí)性能。而Rust語言采用了一種全新的方式:通過所有權(quán)系統(tǒng)管理內(nèi)存。Rust語言編譯器在編譯期間執(zhí)行所有權(quán)規(guī)則檢查,即可明確所有對(duì)象的作用域和生命周期,從而可以在某個(gè)對(duì)象不再被使用時(shí)將其銷毀,并且不影響程序運(yùn)行時(shí)性能。
Rust語言的所有權(quán)系統(tǒng)規(guī)定了下述的所有權(quán)規(guī)則:
1)Rust語言中每個(gè)對(duì)象都有一個(gè)所有者。
2)對(duì)象有且只有一個(gè)所有者。
3)所有者離開作用域后,其所代表的對(duì)象會(huì)被立即銷毀。
4)賦值語句、函數(shù)調(diào)用等會(huì)導(dǎo)致所有權(quán)轉(zhuǎn)移,原有所有者失去對(duì)象的所有權(quán)。
Rust語言的所有權(quán)系統(tǒng)保證了某一時(shí)刻只有一個(gè)變量持有對(duì)象的所有權(quán),避免了數(shù)據(jù)競爭和重復(fù)釋放。所有者離開作用域時(shí)銷毀其對(duì)象的機(jī)制,也防止了內(nèi)存泄露。
在Rust語言的所有權(quán)規(guī)則中,當(dāng)某個(gè)變量被傳入一個(gè)新的作用域中時(shí),變量的所有權(quán)也從原作用域轉(zhuǎn)移到新的作用域;當(dāng)變量返回原作用域時(shí),卻已經(jīng)喪失了原有的所有權(quán)。一個(gè)典型的例子是:某個(gè)變量作為參數(shù)傳入函數(shù),執(zhí)行函數(shù)調(diào)用后,該變量會(huì)失去對(duì)象的所有權(quán),后續(xù)無法通過該變量使用其對(duì)象,這并不符合預(yù)期。因此,Rust語言提供了所有權(quán)借用特性,非所有者的變量或者函數(shù)想要訪問對(duì)象時(shí),通過借用對(duì)象所有者的所有權(quán)以達(dá)到目的,所有權(quán)借用不造成所有權(quán)轉(zhuǎn)移,借用完畢后歸還所有權(quán)。借用所有權(quán)會(huì)讓所有者受如下限制:
1)在不可變借用期間,所有者不能修改資源,并且也不能再進(jìn)行可變借用。
2)在可變借用期間,所有者不能訪問資源,并且也不能再出借所有權(quán)。
Rust語言對(duì)指針的使用進(jìn)行了限制,原生指針(類似C語言的指針)只能在不安全代碼中進(jìn)行使用,針對(duì)安全編程,Rust語言提供了一種更高級(jí)的指針語義——引用。引用與指針的差別在于:指針保存某塊內(nèi)存的地址,引用則可看作某塊內(nèi)存的別名,引用的使用要求滿足Rust語言編譯器各種安全檢查規(guī)則。
在Rust語言的所有權(quán)系統(tǒng)中,引用產(chǎn)生所有權(quán)的借用,引用在離開作用域時(shí)歸還借用的所有權(quán)。雖然使用引用與直接使用被引用者一樣自然,但是受如下規(guī)則的限制,違反規(guī)則中的任何一條,編譯器便會(huì)報(bào)錯(cuò)并給出提示。
1)在某個(gè)時(shí)刻,某個(gè)變量要么擁有一個(gè)可變引用,要么擁有多個(gè)不可變引用。
2)引用必須保證有效。
3)引用的生命周期不能超過被引用者的生命周期。
引用避免了空指針和野指針的情況,防止了數(shù)據(jù)競爭。引用的生命周期檢查保證了數(shù)據(jù)不會(huì)在其引用離開其作用域之前被釋放而變成懸垂?fàn)顟B(tài),避免了C語言中懸空指針的問題。
Rust語言優(yōu)于C語言的安全特性,不輸于C語言的性能,以及具備嵌入式開發(fā)的能力,使其十分適合用于執(zhí)行開發(fā)安全功能的嵌入式軟件,結(jié)合核電儀控軟件安全可靠的發(fā)展需求,使得Rust語言在核電儀控領(lǐng)域的應(yīng)用成為可能。
目前在核電儀控領(lǐng)域,核電儀控軟件為了滿足功能安全中的確定性要求,大多采用定周期的裸機(jī)系統(tǒng)。隨著核電儀控系統(tǒng)自動(dòng)化、智能化發(fā)展,核電儀控軟件功能日益增多,裸機(jī)系統(tǒng)的弊端也逐漸凸顯出來。
1)傳統(tǒng)的裸機(jī)系統(tǒng)按照順序執(zhí)行各功能,隨著軟件功能的增多,軟件周期增大,導(dǎo)致安全相關(guān)的功能不能及時(shí)執(zhí)行,無法滿足安全功能的實(shí)時(shí)性要求。
2)裸機(jī)系統(tǒng)各功能模塊存在耦合,系統(tǒng)功能的增多導(dǎo)致軟件復(fù)雜性變高,可維護(hù)性變差。盡管采用安全級(jí)操作系統(tǒng)可避免裸機(jī)系統(tǒng)帶來的問題,然而使用不安全的C語言開發(fā)一款安全級(jí)操作系統(tǒng)無疑是一項(xiàng)成本極高且難度極大的任務(wù),而且C語言的不安全特性也導(dǎo)致其開發(fā)的操作系統(tǒng)的安全性受到質(zhì)疑。因此,時(shí)至今日,在功能安全相關(guān)的核電儀控軟件中幾乎未見到操作系統(tǒng)的身影。
Rust語言的出現(xiàn)可能改變這一現(xiàn)狀。一方面,Rust語言具有豐富的高級(jí)語言特性,如函數(shù)式編程語言的模式匹配和類型推導(dǎo),基于trait的泛型機(jī)制,強(qiáng)大的抽象能力等,非常適合大型系統(tǒng)編程場景,采用Rust語言開發(fā)操作系統(tǒng)變得相對(duì)簡單;另一方面,Rust語言提供強(qiáng)大的類型系統(tǒng)和獨(dú)特的生命周期管理,實(shí)現(xiàn)了編譯內(nèi)存管理,保證內(nèi)存安全和線程安全的同時(shí),運(yùn)行效率還能與C/C++保持在同一級(jí)別。
基于Rust語言開發(fā)的安全級(jí)操作系統(tǒng),在成本、安全、效率等方面存在較大優(yōu)勢和競爭力,未來應(yīng)用于核電儀控軟件領(lǐng)域的可能性較大。一個(gè)典型的基于Rust語言的安全級(jí)操作系統(tǒng)如圖3所示。
圖3 Rust語言安全級(jí)操作系統(tǒng)架構(gòu)Fig.3 Rust language security level operating system architecture
裸機(jī)系統(tǒng)中沒有嚴(yán)格對(duì)應(yīng)用程序和設(shè)備驅(qū)動(dòng)程序進(jìn)行隔離,整個(gè)系統(tǒng)核心由一個(gè)大的死循環(huán)構(gòu)成,系統(tǒng)所有功能模塊均包含在死循環(huán)內(nèi),應(yīng)用程序和設(shè)備驅(qū)動(dòng)程序之間存在直接或間接的功能耦合,難以實(shí)現(xiàn)獨(dú)立安全的應(yīng)用程序。
基于Rust語言開發(fā)的操作系統(tǒng),使用Rust語言開發(fā)安全級(jí)應(yīng)用程序變得簡單。安全級(jí)操作系統(tǒng)實(shí)現(xiàn)了應(yīng)用與驅(qū)動(dòng)程序、應(yīng)用程序與應(yīng)用程序之間的安全隔離,保證了單個(gè)應(yīng)用程序的安全性、獨(dú)立性,也避免了單個(gè)應(yīng)用程序故障導(dǎo)致系統(tǒng)失效的風(fēng)險(xiǎn)。
Rust語言作為一門新的編程語言,目前在核電領(lǐng)域的應(yīng)用幾乎處于空白,沒有成熟的項(xiàng)目和成功的經(jīng)驗(yàn)作為參考和支撐,不免令人對(duì)其應(yīng)用于核電儀控領(lǐng)域的安全性、適用性和可靠性產(chǎn)生擔(dān)憂。
C語言在核電領(lǐng)域的廣泛應(yīng)用,配套的相關(guān)行業(yè)標(biāo)準(zhǔn)十分完善。IEC61508-7[5]的附錄C4.5中對(duì)C語言用于開發(fā)功能安全系統(tǒng)給出了限定條件:“具有子集和編碼標(biāo)準(zhǔn)和靜態(tài)分析工具的C”。為了滿足此條件,制定了MISRA-2004、GJB5369-2005《航天型號(hào)軟件C語言安全子集》、GB/T28169-2011《嵌入式軟件C語言編碼規(guī)范》等一系列C語言相關(guān)標(biāo)準(zhǔn)和規(guī)范。
而Rust語言目前暫未納入IEC61508推薦用于開發(fā)功能安全系統(tǒng)的系列編程語言中,因此相關(guān)的行業(yè)標(biāo)準(zhǔn)缺失,缺少相關(guān)標(biāo)準(zhǔn)進(jìn)行指導(dǎo)和規(guī)約,其應(yīng)用與核電儀控領(lǐng)域的競爭力被削弱。
Rust語言目前僅具備基礎(chǔ)的編譯、調(diào)試等生產(chǎn)工具,尚未提供用于功能安全開發(fā)活動(dòng)的工具支持,如經(jīng)過安全認(rèn)證的開發(fā)工具、驗(yàn)證軟件、靜態(tài)解析和自動(dòng)化測試工具等,作為功能安全領(lǐng)域軟件開發(fā)活動(dòng)必需的生產(chǎn)工具,其重要性不言而喻,使用未經(jīng)認(rèn)證的開發(fā)工具所開發(fā)的軟件不能滿足安全要求。因此,要使得Rust語言廣泛應(yīng)用于核電儀控領(lǐng)域,完善安全相關(guān)的開發(fā)工具是前提。
目前大部分的核電儀控軟件開發(fā)采用C語言,熟悉Rust語言的開發(fā)人員較少,Rust語言應(yīng)用于核電儀控領(lǐng)域,需要開發(fā)人員由C語言遷移到Rust語言,遷移存在幾個(gè)難點(diǎn):一是開發(fā)人員缺少Rust語言的開發(fā)經(jīng)驗(yàn),需要從頭學(xué)起;二是Rust語言和C語言的語言特性差異較大,因此在軟件設(shè)計(jì)和軟件編碼上均存在較大差異,開發(fā)人員需要編程思維的轉(zhuǎn)變;三是Rust語言在市場應(yīng)用方面還比較小眾,可用的功能函數(shù)庫及開源庫較少,需要開發(fā)人員自己造輪子,對(duì)開發(fā)人員提出了更高的要求。
Rust語言的特性與核電儀控軟件發(fā)展需求完美契合,本文對(duì)Rust語言在核電儀控軟件的應(yīng)用可能性進(jìn)行了探討,旨在推動(dòng)Rust語言在核電儀控領(lǐng)域的應(yīng)用發(fā)展??梢灶A(yù)見,隨著Rust語言自身的發(fā)展,相關(guān)標(biāo)準(zhǔn)的出臺(tái)以及配套工具的完善,各類相關(guān)機(jī)構(gòu)和組織都將對(duì)Rust語言在核電儀控領(lǐng)域的應(yīng)用進(jìn)行積極的探索和研究,Rust語言會(huì)在核電儀控領(lǐng)域展現(xiàn)出強(qiáng)大的活力。