Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

push_cloned may drop uninitialized or double-free if clone panics. #5

Closed
ammaraskar opened this issue Feb 22, 2021 · 3 comments
Closed

Comments

@ammaraskar
Copy link

Hi there, we (Rust group @sslab-gatech) are scanning crates on crates.io for potential soundness bugs. We noticed a panic safety issue in the StackA::push_cloned function:

stack_dst-rs/src/stack.rs

Lines 153 to 161 in 807e9d4

pub fn push_cloned(&mut self, v: &[T]) -> Result<(), ()> {
self.push_inner(&v).map(|d| unsafe {
let mut ptr = d.as_mut_ptr() as *mut T;
for val in v {
ptr::write(ptr, val.clone());
ptr = ptr.offset(1);
}
})
}

The self.push_inner function increases the length of the stack, but between the element being written and this increased length the val.clone() function is called which can leave the stack in a longer state but missing an element. Here's a simple demonstration of this issue:

#![forbid(unsafe_code)]

use stack_dst::StackA;

#[derive(Debug)]
struct DropDetector(u32);

impl Drop for DropDetector {
    fn drop(&mut self) {
        println!("Dropping {}", self.0);
    }
}

impl Clone for DropDetector {
    fn clone(&self) -> Self { panic!("Panic in clone()") }
}

fn main() {
    let mut stack = StackA::<[DropDetector], [usize; 9]>::new();
    stack.push_stable([DropDetector(1)], |p| p).unwrap();
    stack.push_stable([DropDetector(2)], |p| p).unwrap();

    println!("Popping off second drop detector");
    let second_drop = stack.pop();

    println!("Pushing panicky-clone");
    stack.push_cloned(&[DropDetector(3)]).unwrap();
}

This outputs:

Popping off second drop detector
Dropping 2
Pushing panicky-clone
thread 'main' panicked at 'Panic in clone()', src/main.rs:29:31
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Dropping 3
Dropping 2
Dropping 1

Return code 101

Notice that Dropping 2 is printed twice, indicating a double-free.

@thepowersgang
Copy link
Owner

Good spot, fix incoming

@ammaraskar
Copy link
Author

Thanks for the quick fix, would you mind publishing a release with it included?

@thepowersgang
Copy link
Owner

Release v0.6.1 tagged and published

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants