今日(7.19)@開源中國 一則消息引發熱議:微軟計劃將 Rust 作為 C 和 C++ 的安全替代品

根據微軟安全響應中心提供的數據,所有微軟年度補丁中約有 70% 是針對內存安全漏洞的修復程序。這樣高的百分比是因為 Windows 和大多數其他微軟產品主要使用 C 和 C++ 編寫,這兩種「內存不安全」(memory-unsafe)的編程語言允許開發人員對內存地址進行細粒度控制,並且可以執行代碼。管理內存執行的開發人員代碼中的一個漏洞可能導致一系列內存安全錯誤,攻擊者可以利用這些錯誤帶來危險和侵入性後果,例如遠程代碼執行或特權提升漏洞。於是,探索使用諸如 Rust 之類的內存安全(memory-safe)語言被提上日程,這或將成為創建更安全的微軟應用程序的替代方法。(xplanet,公眾號:開源中國,微軟擁抱Rust,以作為C和C++的安全替代方案)

其實,自操作系統誕生以來,系統級主流編程語言,從彙編語言到C++,已經發展了近50 個年頭,但依然存在兩個難題:

  • 很難編寫內存安全的代碼。
  • 很難編寫線程安全的代碼。

這兩個難題存在的本質原因是C/C++屬於類型不安全的語言,它們薄弱的內存管理機制導致了很多常見的漏洞。

可Rust 能解決這個問題多虧了Rust 語言所遵循的設計哲學之一:內存安全

現代編程語言早已發展到了「程序即類型證明」的階段,類型系統基本已經成為了各大編程語言的標配,尤其是近幾年新出現的編程語言。類型系統提供了以下好處:

  • 允許編譯器偵測無意義甚至無效的代碼,暴露程序中隱含的錯誤。
  • 可以為編譯器提供有意義的類型信息,幫助優化代碼。
  • 可以增強代碼的可讀性,更直白地闡述開發者的意圖。
  • 提供了一定程度的高級抽象,提升開發效率。

一般來說,一門語言只要保證類型安全,就可以說它是一門安全的語言。簡單來說,類型安全是指類型系統可以保證程序的行為是意義明確、不出錯的。像C/C++語言的類型系統就不是類型安全的,因為它們並沒有對無意義的行為進行約束。一個最簡單的例子就是數組越界,在C/C++語言中並不對其做任何檢查,導致發生了語言規範規定之外的行為,也就是未定義行為(Undefined Behavior)。而這些未定義行為恰恰是漏洞的溫牀。所以,像C/C++這種語言就是類型不安全的語言。

Rust 語言如果想保證內存安全,首先要做的就是保證類型安全。

在諸多編程語言中,OCaml 和Haskell 是公認的類型安全的典範,它們的類型系統不僅僅有強大的類型論理論「背書」,而且在實踐生產環境中也久經考驗。所以,Rust 語言借鑒了它們的類型系統來保證類型安全,尤其是Haskell,你能在Rust 語言中看到更多Haskell 類型系統的影子。

然而,直接使用Haskell 的類型系統也無法解決內存安全問題。類型系統的作用是定義編程語言中值和表達式的類型,將它們歸類,賦予它們不同的行為,指導它們如何相互作用。

Haskell 是一門純函數式編程語言,它的類型系統主要用於承載其「純函數式」的思想,是範疇論的體現。而對於Rust 來說,它的類型系統要承載其「內存安全」的思想。所以,還需要有一個安全內存管理模型,並通過類型系統表達出來,才能保證內存安全。

簡單來說,就是不會出現內存訪問錯誤。只有當程序訪問未定義內存的時候才會產生內存錯誤。一般來說,發生以下幾種情況就會產生內存錯誤:

  • 引用空指針。
  • 使用未初始化內存。
  • 釋放後使用,也就是使用懸垂指針。
  • 緩衝區溢出,比如數組越界。
  • 非法釋放已經釋放過的指針或未分配的指針,也就是重複釋放。

這些情況之所以會產生內存錯誤,是因為它們都訪問了未定義內存。為了保證內存安全,Rust 語言建立了嚴格的安全內存管理模型:

  • 所有權系統。每個被分配的內存都有一個獨佔其所有權的指針。只有當該指針被銷毀時,其對應的內存才能隨之被釋放。
  • 借用和生命週期。每個變數都有其生命週期,一旦超出生命週期,變數就會被自動釋放。如果是借用,則可以通過標記生命週期參數供編譯器檢查的方式,防止出現懸垂指針,也就是釋放後使用的情況。

其中所有權系統還包括了從現代C++那裡借鑒的RAII 機制,這是Rust 無GC 但是可以安全管理內存的基石。

建立了安全內存管理模型之後,再用類型系統表達出來即可。Rust 從Haskell 的類型系統那裡借鑒了以下特性:

  • 沒有空指針
  • 默認不可變
  • 表達式
  • 高階函數
  • 代數數據類型
  • 模式匹配
  • 泛型
  • trait 和關聯類型
  • 本地類型推導

為了實現內存安全,Rust 還具備以下獨有的特性:

  • 仿射類型(Affine Type),該類型用來表達Rust 所有權中的Move 語義。
  • 借用、生命週期。

藉助類型系統的強大,Rust 編譯器可以在編譯期對類型進行檢查,看其是否滿足安全內存模型,在編譯期就能發現內存不安全問題,有效地阻止未定義行為的發生。

內存安全的Bug 和並發安全的Bug 產生的內在原因是相同的,都是因為內存的不正當訪問而造成的。同樣,利用裝載了所有權的強大類型系統,Rust 還解決了並發安全的問題。Rust編譯器會通過靜態檢查分析,在編譯期就檢查出多線程並發代碼中所有的數據競爭問題。

微軟擁抱Rust 並非偶然,其實早先微軟已在Azure IoT 網路上部分使用了Rust。目前在商業領域,Rust 的重磅商業用戶還包括:

  • Amazon,使用Rust 作為構建工具。
  • Atlassian,在後端使用Rust。
  • Dropbox,在前後端均使用了Rust。
  • Facebook,使用Rust 重寫了源碼管理工具。
  • Google,在Fuchsia 項目中部分使用了Rust。
  • npm,在其核心服務上使用了Rust。
  • RedHat,使用Rust 創建了新的存儲系統。
  • Reddit,使用Rust 處理評論。
  • Twitter,在構建團隊中使用Rust。

……

Rust 的前景越來越明朗,未來Rust 將大有可為。學習Rust勢在必行!


本文內容來自《Rust編程之道》一書。本書基於Rust2018大版本,從Rust設計理念入手結合豐富實踐案例,幫助改善你的Rust學習曲線。

------------

更多科技資訊請見微信公眾號:博文視點Broadview(微信號:bvbooks)


推薦閱讀:
相關文章