100% 確定清空並釋放記憶體

Wu Yu Wei published on
3 min, 510 words

安全地清空記憶體通常不是我們會在意的事情,在 Rust 因為所有物件都有其生命週期並會在 scope 結束時就被釋放掉,但如果今天的對象是敏感的資訊像是 secret 的話我們就得另當別論了。編譯器一直以來都會自動幫我們最佳化,所以他們很可能會把一些他們認為不必要的呼叫給優化掉,另外如果是在 stack 上的變數則是直到下一次存取才會覆蓋掉。而且當你想交叉編譯的話情況會變得更複雜,當然如果你願意的話可以在 regression 特地編出所有目標然後用 valgrind 之類的檢查有沒有記憶體洩漏,但這似乎太大費周章了。通常這樣的情形我們可以簡單有幾行程式顯式地確定有被清空釋放掉。

首先我們知道有 Drop trait 讓物件在 drop 時可以多作一些事,可以把這個想成相當於 Destructor 的概念。再來 Rust 雖然還沒有 volatile 的語法,但是在標準函式庫中的 ptr 模組就已經有 volatile 的方法可以使用,而我們可以在釋放前用 ptr::write_volatile 清掉我們的敏感資訊,而且確定不會被編譯器重新排列或者優化掉。最後我們加上 atomic::compiler_fence 來確保所有的存取在這時看到的 secret 已經是被清掉的訊息了。這樣一來就能 100% 確定我們想釋放的記憶體是真的會被 drop 掉。

Playground link

use std::sync::atomic::compiler_fence;
use std::sync::atomic::Ordering;
use std::ptr::write_volatile;
use std::ops::Drop;

#[derive(Default)]
struct Secret(String);

impl Drop for Secret {
    fn drop(&mut self) {
        unsafe{ write_volatile(self, Default::default()); }
        compiler_fence(Ordering::SeqCst);
        println!("Secret is dropped for sure!");
    }
}

fn main() {
    let _secret = Secret(String::from("Sensitive info"));
}