Skip to content

Instantly share code, notes, and snippets.

@eira-fransham
Last active November 27, 2019 03:35
Show Gist options
  • Save eira-fransham/ffa55d8e69377a64c9708e07c2db2a1b to your computer and use it in GitHub Desktop.
Save eira-fransham/ffa55d8e69377a64c9708e07c2db2a1b to your computer and use it in GitHub Desktop.
Explanation of unsoundness in `SmallVec::insert_many`
extern crate smallvec;
use smallvec::SmallVec;
struct Printer(usize);
impl Drop for Printer {
fn drop(&mut self) {
println!("Dropping {}", self.0);
}
}
struct Bad;
impl Iterator for Bad {
type Item = Printer;
fn size_hint(&self) -> (usize, Option<usize>) {
(1, None)
}
fn next(&mut self) -> Option<Printer> {
panic!()
}
}
// If you run this in the Rust playground this prints:
// Dropping 0
// Dropping 0
// Dropping 1
//
// Obviously this is fine with this dummy struct but if you
// used `Box` this would cause a double-free.
//
// What happens is that before iterating,
// `SmallVec::insert_many` moves the existing elements, so
// if you start with an arry that looks like this:
// [a, b, c, (uninitialised)]
// ^-----^ The elements between these points are at
// indexes less than `len` and so can be
// accessed.
//
// You (temporarily) get an array that looks like this:
// [a, b, b, c, (uninitialised)]
// ^-----^ Accessible elements
//
// When the iterator panics, the `SmallVec` iterates over
// the accessible elements and drops each of them in turn,
// which is bad when there are two copies of the same value
// (you get a double-drop).
fn main() {
// This doesn't need to be 0, this is unsound with any
// value here.
let vec: SmallVec<[Printer; 0]> = vec![
Printer(0),
Printer(1),
Printer(2),
].into();
vec.insert_many(0, Bad);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment