我发现mem::drop
不必在调用它的位置附近运行,这很可能导致在昂贵的计算过程中保留了Mutex
或RwLock
保护。如何控制何时调用drop
?
作为一个简单的示例,我通过使用unsafe { ::std::intrinsics::drop_in_place(&mut s); }
而不是简单的::std::mem::drop(s)
对加密 Material 的零滴工作进行了以下测试。
#[derive(Debug, Default)]
pub struct Secret<T>(pub T);
impl<T> Drop for Secret<T> {
fn drop(&mut self) {
unsafe { ::std::intrinsics::volatile_set_memory::<Secret<T>>(self, 0, 1); }
}
}
#[derive(Debug, Default)]
pub struct AnotherSecret(pub [u8; 32]);
impl Drop for AnotherSecret {
fn drop(&mut self) {
unsafe { ::std::ptr::write_volatile::<$t>(self, AnotherSecret([0u8; 32])); }
assert_eq!(self.0,[0u8; 32]);
}
}
#[cfg(test)]
mod tests {
macro_rules! zeroing_drop_test {
($n:path) => {
let p : *const $n;
{
let mut s = $n([3u8; 32]); p = &s;
unsafe { ::std::intrinsics::drop_in_place(&mut s); }
}
unsafe { assert_eq!((*p).0,[0u8; 32]); }
}
}
#[test]
fn zeroing_drops() {
zeroing_drop_test!(super::Secret<[u8; 32]>);
zeroing_drop_test!(super::AnotherSecret);
}
}
如果我使用
::std::mem::drop(s)
或什至此测试失败#[inline(never)]
pub fn drop_now<T>(_x: T) { }
使用
drop_in_place
进行缓冲区清零的测试显然很好,但我担心在drop_in_place
或Mutex
防护程序上调用RwLock
可能会导致释放后使用。可以使用这种方法来处理这两个守卫:
#[inline(never)]
pub fn drop_now<T>(t: mut T) {
unsafe { ::std::intrinsics::drop_in_place(&mut t); }
unsafe { ::std::intrinsics::volatile_set_memory::<Secret<T>>(&t, 0, 1); }
}
最佳答案
https://github.com/rust-lang/rfcs/issues/1850的回答:
在 Debug模式下,对::std::mem::drop(s)
的任何调用实际上都会在堆栈上移动s
,因此p
指向不会被擦除的旧副本。 unsafe { ::std::intrinsics::drop_in_place(&mut s); }
起作用是因为它不会移动s
。
通常,没有办法阻止LLVM在堆栈上移动值,或者在移动它们后将其设置为零,因此您绝不能将密码敏感的数据放在堆栈上。相反,您必须Box
任何敏感数据,例如说
#[derive(Debug, Default)]
pub struct AnotherSecret(Box<[u8; 32]>);
impl Drop for AnotherSecret {
fn drop(&mut self) {
*self.0 = [0u8; 32];
}
}
Mutex
或RwLock
应该没有问题,因为当它们被drop
编码时,它们可以安全地在堆栈上留下残留物。关于multithreading - 有什么安全的方法可以确保在进行一些昂贵的计算之前发生任意丢弃?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41560442/