Skip to content

Commit f7eb544

Browse files
committed
Fix undefined behavior in Vec::truncate()
1 parent d304960 commit f7eb544

File tree

1 file changed

+18
-7
lines changed

1 file changed

+18
-7
lines changed

src/vec.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,24 @@ impl<T, const N: usize> Vec<T, N> {
263263

264264
/// Shortens the vector, keeping the first `len` elements and dropping the rest.
265265
pub fn truncate(&mut self, len: usize) {
266-
// drop any extra elements
267-
while len < self.len {
268-
// decrement len before the drop_in_place(), so a panic on Drop
269-
// doesn't re-drop the just-failed value.
270-
self.len -= 1;
271-
let len = self.len;
272-
unsafe { ptr::drop_in_place(self.as_mut_slice().get_unchecked_mut(len)) };
266+
// This is safe because:
267+
//
268+
// * the slice passed to `drop_in_place` is valid; the `len > self.len`
269+
// case avoids creating an invalid slice, and
270+
// * the `len` of the vector is shrunk before calling `drop_in_place`,
271+
// such that no value will be dropped twice in case `drop_in_place`
272+
// were to panic once (if it panics twice, the program aborts).
273+
unsafe {
274+
// Note: It's intentional that this is `>` and not `>=`.
275+
// Changing it to `>=` has negative performance
276+
// implications in some cases. See rust-lang/rust#78884 for more.
277+
if len > self.len {
278+
return;
279+
}
280+
let remaining_len = self.len - len;
281+
let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
282+
self.len = len;
283+
ptr::drop_in_place(s);
273284
}
274285
}
275286

0 commit comments

Comments
 (0)