Description
I commonly see people call vec.set_len()
to grow a vector and then fill the uninitialized data as a slice. This is dangerous because it exposes uninitialized memory to be read and dropped on panic. See Here's My Type, So Initialize Me Maybe for more details - anything that applies to mem::uninitialized
also applies to regions exposed via vec.set_len()
.
Here are some problematic examples:
Creating Vec to create a slice
Wrong way:
let mut v: Vec<u8> = Vec::with_capacity(32);
unsafe {v.set_len(32)}; // definitely UB for most types, possibly UB for u8
write_something_to_slice(v.as_mut_slice());
Right way:
let mut v: Vec<u8> = vec![0; 32];
write_something_to_slice(v.as_mut_slice());
Doing this for any type where not every bit pattern is valid (like bool
) or for Drop
types is instant UB.
For types with any valid bit pattern it's less clear-cut, but still very dangerous and has already led to security vulnerabilites in real code. It's also a common pattern to pass such slices to std::io::Read::read()
, which is unsound.
Note that for u8
and friends the use of vec![0; len]
is already encouraged through some lints, see #3237, but there are currently no lints for unsafe variants of the code. Using vec![0; len]
and reusing the vector for subsequent operations should be as fast as using uninit, but safe.
Appending to vector while borrowing its contents
Wrong way:
let mut v: Vec<bool> = vec![false, false, true, true];
v.reserve(4);
unsafe {v.set_len(8)};
for i in 0..4 {
v[i+4] = ! v[i];
}
Right way:
let mut v: Vec<bool> = vec![false, false, true, true];
v.reserve(4);
let v_ptr = v.as_mut_ptr();
unsafe {
for i in 0..4 {
std::ptr::write(v_ptr.add(i+4), ! v[i]);
}
v.set_len(8);
}
Here's a real-world example of such code, albeit with u8
: https://github.com/image-rs/inflate/blob/1a810dba8467fc9cf1e8c60155d3266790072220/src/lib.rs#L663
This is instant UB for types where not all bit patterns are valid. This is also an exploitable security vulnerability if this is done for Drop
types, or if *v_ptr = value
is used instead of ptr::write(v_ptr, value)
Sadly the wrong pattern is the best available pattern for types such as u8
(at least in the stdlib). Using ptr::write is not needed for such types, and you lose the bounds checks too if you use pointers. I've opened an RFC to address this, but it's not getting much attention.
Other patterns
I'm probably missing something, so please add other problematic examples.
cc @RalfJung