我想像
*const
一样使用Rust原始指针来重现释放后使用的安全问题,但是当我使用原始指针访问字段或调用已释放的结构变量的函数时,没有发生恐慌。
这是我的代码:
new
方法用于更方便地创建结构。 as_ptr
方法用于返回结构本身的原始指针。
pub struct Foo{
data: Vec<u8>,
}
impl Foo{
pub fn new(data: &[u8]) -> Foo{
Foo{ data: data.to_vec() }
}
pub fn as_ptr(&self) -> *const Foo{
// return a raw pointer of this struct
self as *const Foo
}
pub fn test(&self){
println!("test");
}
}
上面的test
方法和下面的use_raw_ptr
方法是为了重现免费问题后的使用。
pub unsafe fn use_raw_ptr(ptr: *const Foo){
println!("{:?}", (*ptr).data);
}
测试时,我在
Foo
块中创建了一个foo
结构变量match
,我想这应该是foo
变量的范围。如果foo
合法,则p
的原始指针被传递给data
。在match
块之后,我尝试使用p
调用test
的函数Foo
并访问data
的foo
字段,我想应该出现自由后使用问题,因为foo
应该是已经释放了。
fn main(){
let data: Option<&[u8]> = Some(b"abc");
let p = match data{
Some(data) => {
let foo = Foo::new(data);
foo.as_ptr();
}
None => std::ptr::null(),
};
unsafe {(*p).test()};
println!("{:?}", data);
unsafe{ use_raw_ptr(p) };
}
但是,执行代码时没有出现恐慌,这是输出:
test
Some([97, 98, 99])
[192, 230, 41]
还应该提到的是,每次运行代码时
use_raw_ptr
的结果都不同,但打印结果的长度始终与输入数据匹配。请问这是不是因为释放foo
时,还保留了data
的长度等信息?
fn main(){
let data: Option<&[u8]> = Some(b"abcde");
// ... same as above
}
// output
test
Some([97, 98, 99, 100, 101])
[192, 230, 5, 252, 49]
此外,我认为
unsafe{(*p).test()}
的succecc调用的原因是它调用了Foo
结构的函数而不是foo
变量。我不知道这是否正确,我想知道我是否可以在这里使用原始指针访问释放的内存来使程序崩溃。
取消引用悬空指针是未定义的行为。未定义的行为,好吧undefined意味着你不能期待任何行为,因此期望它恐慌是无效的。
换句话说,任何行为,包括但不限于不更改内存,因此值仍然在相同的内存位置,恐慌,让电脑着火,执行Doom™游戏是可能的。