Search code examples
ruststructunsaferaw-pointer

Why Rust's raw pointer can access function and some parts of fields' information of a struct variable out of scope?


I want to use Rust raw pointer like *const to reproduce the security problem of use after free, but when I use the raw pointer to access fields or call the function of a freed struct variable, there's no panic occurred.

Here's my codes:

new method is used to create the struct more conveniently. as_ptr method is used to return the raw pointer of the struct itself.

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 method above and use_raw_ptr method below are intended to reproduce the use after free problem.

pub unsafe fn use_raw_ptr(ptr: *const Foo){
    println!("{:?}", (*ptr).data);
}

When testing, I create a Foo struct variable foo in match blocks, which I suppose should be the scope of the foo variable. Then the raw pointer of foo is passed to p if data is legal. After match block, I try to use p to call the function test of Foo and access data field of foo, where I suppose use after free problem should appear because foo should be freed already.

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) };
}

However, no panic occurrs when executing the codes and here's the output:

test
Some([97, 98, 99])
[192, 230, 41]

It also should be mentioned that the result of use_raw_ptr differs everytime I run the codes, but the length of printed result always match the input data. I wonder if this is because when foo is freed, some information like the length of data is still remained?

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]

Besides, I think that the reason of the succecc call of unsafe{(*p).test()} is that it calls the function of Foo struct instead of foo variable. I don't know if this is corrent and I'm wondering if I can make program panic here using raw pointer to access freed memory.


Solution

  • Dereferencing a dangling pointer is undefined behaviour. Undefined behaviour is, well undefined meaning that you cannot expect any behaviour, exepecting it to panic is therefore not valid.

    Or in other words, any behaviour including but not limited to not changing the memory and therefore the values still being in the same memory locations, panicing, setting the pc on fire, executing a game of Doom™ is possible.