楊 斌,蔣 維,常澤海
(1.西安交通大學核科學與技術學院,西安 710049;2.中國核動力研究設計院核反應堆系統設計技術重點實驗室,成都 610213)
隨著核電領域的自動化和數字化發展,核電儀控軟件在整個核電系統中扮演的角色越來越重要,這對核電儀控軟件提出了更高的安全性和可靠性要求。由于C語言的高性能且能夠直接操作硬件的特性,目前大部分核電儀控軟件采用C語言進行開發。然而,C語言的不安全特性使得開發的軟件系統易出現安全性問題,核電安全級儀控軟件開發過程中必須通過遵循嚴格的安全標準規范以及引入一系列軟件開發活動以規避C語言的安全風險。目前常用的標準有:MSRA-2004、GJB-5369-2005《航天型號軟件C語言安全子集》、GB/T28169-2011《嵌入式軟件C語言編碼規范》[1]等,軟件開發活動包括軟件代碼靜態分析、軟件測試和軟件V&V活動[2]。一方面,檢測工具的檢測能力和準確性直接影響檢測結果,代碼檢測工具存在誤報或漏檢的可能;另一方面,無論是軟件測試還是軟件V&V,均無法保證百分之百的覆蓋率。因此,引入標準規范和軟件開發活動屬于被動防御手段,并不能完全規避C語言的不安全特性帶來的安全風險。
Rust語言自問世以來,致力于成為優雅解決高并發、高安全系統問題的編程語言,適用于大型場景,創造維護能夠保持大型系統完整的邊界,因此更加強調安全性、內存布局控制和并發處理方面的特性,在網絡服務器領域引起極大重視。核電領域因其特殊性,Rust語言應用尚處于探索階段。本文就Rust語言應用于核電安全級儀控軟件開發,以規避C語言的安全風險的可能性進行探討。
C語言是一種不安全的語言,其固有特性易使得所開發的程序存在安全缺陷,同時其極大的靈活性也容易導致開發人員的不安全使用行為,均為軟件帶來了安全隱患,可能導致軟件失效。
在C語言中未對指針的使用加以限制,指針理論上可以訪問任意的內存空間。若訪問了非法或者受保護的內存地址,則會導致內存問題,然而C語言編譯器并未對指針操作進行嚴格的安全性檢查。這帶來了如下幾方面的問題:
1)C語言不對空指針進行檢查,空指針所指向的內存空間一般是受保護的。在軟件中對空指針進行解引用,會引發軟件異常。
2)C語言中不強制要求指針初始化,未初始化的指針即是野指針。野指針的值取決于以前遺留的是什么值,因此它可能指向未知的內存空間,對野指針進行解引用,可能會引發軟件異常,也可能不會。但無論如何,這個行為不屬于預期內的安全行為,存在安全隱患。
3)C語言使用指針訪問動態申請的內存空間,當內存空間使用完畢并進行釋放后,指針仍舊指向已釋放的空間,此時的指針便是懸空指針。它與野指針類似,同樣會操作不屬于自己的內存空間,導致安全問題。圖1展示了對懸空指針進行操作的情況。
C語言內存管理由開發人員通過內存管理函數手動進行分配和釋放,C語言提供了一系列的內存管理函數(如malloc、calloc、recalloc、free等)。若使用不當,會引發內存問題。然而,C編譯器在編譯階段并不能暴露此種情況,導致運行過程中出現內存錯誤,可能引發軟件崩潰。
常見的內存問題有內存泄漏和內存釋放錯誤。動態申請的內存使用完畢后,并未調用相應的釋放函數對其進行釋放,則會造成內存泄漏[3]。C語言無法暴露內存泄漏問題,內存泄漏導致可用的內存越來越少,最終引發程序崩潰,圖2對此種情況進行了展示。內存的分配和釋放要配對,若對一塊內存空間執行重復釋放,也會造成內存釋放錯誤。若指針指向的地址并不是內存分配后的地址,對其執行釋放操作,也是危險的。

圖2 內存泄漏Fig.2 Memory leak
2004年以來,微軟安全響應中心(MSRC)已對所有報告過的微軟安全漏洞進行了分類。根據他們提供的數據,所有微軟每年的補丁中約有70%是針對內存安全漏洞的修復程序,故而微軟正在考慮使用新的內存安全語言替代C/C++語言進行操作系統的開發。
Rust語言是Mozilla主導開發的通用、編譯型語言,專注于安全,尤其是并發安全[4]。設計準則為“安全、并發、實用”,支持函數式、并發式、過程式以及面向對象的編程風格。在語法上和C++類似,但是在設計上保證性能的同時需提供更好的內存安全,通過一系列的手段讓不安全的內存風險在編譯階段暴露,保證了程序運行中的內存安全。
每種編程語言都有其管理使用計算機內存的方式,要么通過開發人員手動進行內存的分配和釋放(如C語言),要么提供垃圾回收機制,在程序運行時不斷尋找不再被使用的內存并進行釋放(如Java語言)。前者將內存管理權交給了開發人員,存在人因的內存安全風險,后者會降低程序運行時性能。而Rust語言采用了一種全新的方式:通過所有權系統管理內存。Rust語言編譯器在編譯期間執行所有權規則檢查,即可明確所有對象的作用域和生命周期,從而可以在某個對象不再被使用時將其銷毀,并且不影響程序運行時性能。
Rust語言的所有權系統規定了下述的所有權規則:
1)Rust語言中每個對象都有一個所有者。
2)對象有且只有一個所有者。
3)所有者離開作用域后,其所代表的對象會被立即銷毀。
4)賦值語句、函數調用等會導致所有權轉移,原有所有者失去對象的所有權。
Rust語言的所有權系統保證了某一時刻只有一個變量持有對象的所有權,避免了數據競爭和重復釋放。所有者離開作用域時銷毀其對象的機制,也防止了內存泄露。
在Rust語言的所有權規則中,當某個變量被傳入一個新的作用域中時,變量的所有權也從原作用域轉移到新的作用域;當變量返回原作用域時,卻已經喪失了原有的所有權。一個典型的例子是:某個變量作為參數傳入函數,執行函數調用后,該變量會失去對象的所有權,后續無法通過該變量使用其對象,這并不符合預期。因此,Rust語言提供了所有權借用特性,非所有者的變量或者函數想要訪問對象時,通過借用對象所有者的所有權以達到目的,所有權借用不造成所有權轉移,借用完畢后歸還所有權。借用所有權會讓所有者受如下限制:
1)在不可變借用期間,所有者不能修改資源,并且也不能再進行可變借用。
2)在可變借用期間,所有者不能訪問資源,并且也不能再出借所有權。
Rust語言對指針的使用進行了限制,原生指針(類似C語言的指針)只能在不安全代碼中進行使用,針對安全編程,Rust語言提供了一種更高級的指針語義——引用。引用與指針的差別在于:指針保存某塊內存的地址,引用則可看作某塊內存的別名,引用的使用要求滿足Rust語言編譯器各種安全檢查規則。
在Rust語言的所有權系統中,引用產生所有權的借用,引用在離開作用域時歸還借用的所有權。雖然使用引用與直接使用被引用者一樣自然,但是受如下規則的限制,違反規則中的任何一條,編譯器便會報錯并給出提示。
1)在某個時刻,某個變量要么擁有一個可變引用,要么擁有多個不可變引用。
2)引用必須保證有效。
3)引用的生命周期不能超過被引用者的生命周期。
引用避免了空指針和野指針的情況,防止了數據競爭。引用的生命周期檢查保證了數據不會在其引用離開其作用域之前被釋放而變成懸垂狀態,避免了C語言中懸空指針的問題。
Rust語言優于C語言的安全特性,不輸于C語言的性能,以及具備嵌入式開發的能力,使其十分適合用于執行開發安全功能的嵌入式軟件,結合核電儀控軟件安全可靠的發展需求,使得Rust語言在核電儀控領域的應用成為可能。
目前在核電儀控領域,核電儀控軟件為了滿足功能安全中的確定性要求,大多采用定周期的裸機系統。隨著核電儀控系統自動化、智能化發展,核電儀控軟件功能日益增多,裸機系統的弊端也逐漸凸顯出來。
1)傳統的裸機系統按照順序執行各功能,隨著軟件功能的增多,軟件周期增大,導致安全相關的功能不能及時執行,無法滿足安全功能的實時性要求。
2)裸機系統各功能模塊存在耦合,系統功能的增多導致軟件復雜性變高,可維護性變差。盡管采用安全級操作系統可避免裸機系統帶來的問題,然而使用不安全的C語言開發一款安全級操作系統無疑是一項成本極高且難度極大的任務,而且C語言的不安全特性也導致其開發的操作系統的安全性受到質疑。因此,時至今日,在功能安全相關的核電儀控軟件中幾乎未見到操作系統的身影。
Rust語言的出現可能改變這一現狀。一方面,Rust語言具有豐富的高級語言特性,如函數式編程語言的模式匹配和類型推導,基于trait的泛型機制,強大的抽象能力等,非常適合大型系統編程場景,采用Rust語言開發操作系統變得相對簡單;另一方面,Rust語言提供強大的類型系統和獨特的生命周期管理,實現了編譯內存管理,保證內存安全和線程安全的同時,運行效率還能與C/C++保持在同一級別。
基于Rust語言開發的安全級操作系統,在成本、安全、效率等方面存在較大優勢和競爭力,未來應用于核電儀控軟件領域的可能性較大。一個典型的基于Rust語言的安全級操作系統如圖3所示。

圖3 Rust語言安全級操作系統架構Fig.3 Rust language security level operating system architecture
裸機系統中沒有嚴格對應用程序和設備驅動程序進行隔離,整個系統核心由一個大的死循環構成,系統所有功能模塊均包含在死循環內,應用程序和設備驅動程序之間存在直接或間接的功能耦合,難以實現獨立安全的應用程序。
基于Rust語言開發的操作系統,使用Rust語言開發安全級應用程序變得簡單。安全級操作系統實現了應用與驅動程序、應用程序與應用程序之間的安全隔離,保證了單個應用程序的安全性、獨立性,也避免了單個應用程序故障導致系統失效的風險。
Rust語言作為一門新的編程語言,目前在核電領域的應用幾乎處于空白,沒有成熟的項目和成功的經驗作為參考和支撐,不免令人對其應用于核電儀控領域的安全性、適用性和可靠性產生擔憂。
C語言在核電領域的廣泛應用,配套的相關行業標準十分完善。IEC61508-7[5]的附錄C4.5中對C語言用于開發功能安全系統給出了限定條件:“具有子集和編碼標準和靜態分析工具的C”。為了滿足此條件,制定了MISRA-2004、GJB5369-2005《航天型號軟件C語言安全子集》、GB/T28169-2011《嵌入式軟件C語言編碼規范》等一系列C語言相關標準和規范。
而Rust語言目前暫未納入IEC61508推薦用于開發功能安全系統的系列編程語言中,因此相關的行業標準缺失,缺少相關標準進行指導和規約,其應用與核電儀控領域的競爭力被削弱。
Rust語言目前僅具備基礎的編譯、調試等生產工具,尚未提供用于功能安全開發活動的工具支持,如經過安全認證的開發工具、驗證軟件、靜態解析和自動化測試工具等,作為功能安全領域軟件開發活動必需的生產工具,其重要性不言而喻,使用未經認證的開發工具所開發的軟件不能滿足安全要求。因此,要使得Rust語言廣泛應用于核電儀控領域,完善安全相關的開發工具是前提。
目前大部分的核電儀控軟件開發采用C語言,熟悉Rust語言的開發人員較少,Rust語言應用于核電儀控領域,需要開發人員由C語言遷移到Rust語言,遷移存在幾個難點:一是開發人員缺少Rust語言的開發經驗,需要從頭學起;二是Rust語言和C語言的語言特性差異較大,因此在軟件設計和軟件編碼上均存在較大差異,開發人員需要編程思維的轉變;三是Rust語言在市場應用方面還比較小眾,可用的功能函數庫及開源庫較少,需要開發人員自己造輪子,對開發人員提出了更高的要求。
Rust語言的特性與核電儀控軟件發展需求完美契合,本文對Rust語言在核電儀控軟件的應用可能性進行了探討,旨在推動Rust語言在核電儀控領域的應用發展。可以預見,隨著Rust語言自身的發展,相關標準的出臺以及配套工具的完善,各類相關機構和組織都將對Rust語言在核電儀控領域的應用進行積極的探索和研究,Rust語言會在核電儀控領域展現出強大的活力。