Rust 如何保證內存安全 - 1 內存管理

簡介

你知道嗎,Chrome 和微軟產品中 70% 的嚴重安全漏洞都是內存安全問題。iOS、macOS 和 Android 也有類似的數字統計。大多數這些安全漏洞的出現是因爲這些軟件系統是用內存不安全的語言 (如 C 和 C++) 編寫的。

在本系列的第一篇文章中,我將討論內存安全的概念,並解釋它如何與內存管理相關聯。在下一篇文章中,我將討論 Rust 中的內存安全性。

內存安全

簡而言之,如果一個程序不訪問無效的內存,它就是內存安全的。例如,在下面的 C 程序中,寫入 buffer[8] 就會產生一個緩衝區溢出的 bug,因爲它寫入超過緩衝區擁有的最後一個字節:

char buffer[8];
buffer[8] = 'x';

當程序讀取超過已分配的內存時,就會發生緩衝區過讀錯誤。緩衝區溢出和過度讀取的 bug 很容易修復,編譯器只需要檢查代碼每次數組訪問的邊界就可以了。雖然有一點性能上的損失,但這是值得的。

當程序讀取超過已分配的內存時,就會發生緩衝區越界錯誤。緩衝區溢出和越界讀取的 bug 很容易修復,編譯器只需要檢查代碼每次數組訪問的邊界就可以了。雖然有一點性能上的損失,但這是值得的。

另一類內存安全 bug 與內存的分配和釋放方式密切相關,使用已釋放內存和重複釋放內存就是其中的兩個 bug。與越界訪問不同,對這類錯誤的補救措施沒有那麼簡單,要了解原因,我們需要理解手動內存管理。

手動內存管理

在 C 語言中,程序員負責管理內存。如果程序員在他們的程序中分配一塊內存,使用 malloc,通過調用一次 free 來釋放該塊內存。 雖然這是一個非常簡單的規則,但在任何重要的程序中遵循它是非常困難的。即使是世界頂級的 C 程序員 - Linux 內核開發人員也會犯這些錯誤,雖然很少。既然手動內存管理如此棘手,那麼我們能做些什麼來解決它嗎?

垃圾收集

內存管理是否可以自動化,從而減輕程序員的負擔?垃圾收集就是一種嘗試,在像 Java 和 Go 這樣的垃圾收集語言中,程序員可以自由分配內存,但沒有義務釋放它。垃圾收集器跟蹤已分配的不再使用的內存,並定期回收它。雖然它可以提供內存安全,但由於垃圾回收器在後臺運行,因此需要付出性能上的代價。

此外,程序員對垃圾收集器的控制非常有限,使得對延遲敏感的程序 (如自動交易系統) 很難將延遲維持在閾值以下。這種控制的缺失也延伸到了該語言的其他領域,例如,程序員也放棄了對是在堆棧上分配空間還是在堆上分配空間的控制。幸運的是,有一種替代垃圾收集的方法。

RAII

RAII 表示資源獲取即初始化,我發現這個首字母縮略詞很難解釋它的作用。換個說法要簡單得多——當程序中的一個變量不再使用時,釋放該變量所擁有的內存。如果編譯器能夠進行這種分析,那麼它就可以插入適當的代碼來釋放內存。這種習慣用法最早起源於 c++,在 c++ 中,當變量的生命週期結束時,編譯器調用生成的析構函數釋放內存。

這種方法有幾個好處。首先,內存被更確定地釋放——在變量超出作用域之後。第二,這種技巧並不侷限於內存。其他資源如文件句柄、數據庫連接等也可以使用完全相同的方法進行清理。這與垃圾收集類語言相反,垃圾收集只負責內存。垃圾收集語言中的其他資源仍然需要手動清理。

看起來 RAII 解決了所有與內存管理相關的內存安全問題,事實上,基於 RAII 的現代 c++ 智能指針就是解決內存管理問題的,但是 c++ 仍然有不足之處。在下一部分中,我將討論 Rust 是如何比 c++ 更好地實現內存安全的。

總結

在本文中,介紹了內存安全的概念。討論了一些內存安全 bug 是如何與內存管理相關的,以及一些避免進行手動內存管理的技術。在下一部分中,將重點討論 Rust 的內存安全方法。

本文翻譯自:

https://hashrust.com/blog/memory-safey-in-rust-part-1/

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kpwMPHYxKZUNDf10WZsi-Q