Rust 标准库没有内置防止 Swap 的 secret_string 类型,必须结合 mlock 系统调用或专用安全 crate 手动锁定内存页。适用场景为处理密码、私钥等高敏感数据,风险边界在于锁定内存会消耗物理 RAM 且可能导致进程因资源不足被杀。
先说结论:防止 Swap 的核心是调用 mlock 锁定物理页,而非依赖 Rust 所有权机制。
- 先判断:确认数据是否值得消耗物理内存锁定
- 优先做:使用 libc::mlock 或安全 crate 锁定分配区域
- 再验证:检查/proc/self/status 确认 VmLck 变化
快速处理思路
通过 libc 调用 mlock 锁定存放敏感数据的堆内存区域,并在 Drop trait 中确保解锁和清零。
use libc::{mlock, munlock};
// 分配内存后立即锁定
unsafe {
mlock(ptr as *const libc::c_void, len);
}
// 释放前解锁
unsafe {
munlock(ptr as *const libc::c_void, len);
}为什么会这样
Linux 内核在内存不足时会将不活跃的内存页移动到磁盘交换空间 (Swap),导致敏感数据持久化残留。
mlock 系统调用告诉内核不要让指定范围的内存页被交换到磁盘,确保数据仅存在于物理 RAM 中。Rust 的所有权系统管理生命周期,但不干预内核的页交换策略,因此必须显式调用系统 API。
分步处理
1. 引入依赖:在 Cargo.toml 添加 libc 或专用安全 crate。
2. 分配内存:使用 Vec<u8> 或 Box<[u8]> 分配足够空间。
3. 锁定内存:调用 mlock 锁定指针指向的区域,检查返回值是否为 0。
4. 实现 Drop:在结构体析构时先清零数据,再调用 munlock 解锁。
5. 错误处理:mlock 失败时应立即清零并终止敏感操作,避免数据暴露在未锁定内存中。
怎么验证是否生效
运行程序后,在终端执行 cat /proc/self/status | grep VmLck,观察 VmLck 值是否随敏感数据分配而增加。
若 VmLck 大小为 0 kB,说明锁定未生效;若数值增加且接近锁定字节数,说明内存已被锁定。
常见坑
1. 资源限制:默认用户锁定内存限制可能过小,需用 ulimit -l 调整,否则 mlock 返回 ENOMEM。
2. 过度锁定:锁定过多内存会导致系统整体性能下降,甚至触发 OOM Killer 杀死进程。
3. 忘记清零:mlock 仅防止交换,不防止内存读取,释放前必须手动清零敏感数据。
常见问题
Rust 的 String 类型会自动防止 Swap 吗
不会。String 只是堆分配内存,内核仍可能将其交换到磁盘,必须手动锁定。
mlock 锁定后数据就绝对安全了吗
不是。mlock 仅防止磁盘交换,无法防御物理内存攻击或进程内未授权读取,需结合加密和清零。
参考来源
别再让敏感数据溜进 Swap 了:手把手教你用 mlock/mlockall 保护内存中的密码和密钥