Skip to content

Commit 9998ba0

Browse files
committed
Fix potential buffer overflow in insert_many
Fixes #252.
1 parent 0b2b4e5 commit 9998ba0

File tree

3 files changed

+38
-25
lines changed

3 files changed

+38
-25
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "smallvec"
3-
version = "1.6.0"
3+
version = "1.6.1"
44
edition = "2018"
55
authors = ["The Servo Project Developers"]
66
license = "MIT/Apache-2.0"

src/lib.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -1009,21 +1009,24 @@ impl<A: Array> SmallVec<A> {
10091009
/// Insert multiple elements at position `index`, shifting all following elements toward the
10101010
/// back.
10111011
pub fn insert_many<I: IntoIterator<Item = A::Item>>(&mut self, index: usize, iterable: I) {
1012-
let iter = iterable.into_iter();
1012+
let mut iter = iterable.into_iter();
10131013
if index == self.len() {
10141014
return self.extend(iter);
10151015
}
10161016

10171017
let (lower_size_bound, _) = iter.size_hint();
10181018
assert!(lower_size_bound <= core::isize::MAX as usize); // Ensure offset is indexable
10191019
assert!(index + lower_size_bound >= index); // Protect against overflow
1020-
self.reserve(lower_size_bound);
1020+
1021+
let mut num_added = 0;
1022+
let old_len = self.len();
1023+
assert!(index <= old_len);
10211024

10221025
unsafe {
1023-
let old_len = self.len();
1024-
assert!(index <= old_len);
1026+
// Reserve space for `lower_size_bound` elements.
1027+
self.reserve(lower_size_bound);
10251028
let start = self.as_mut_ptr();
1026-
let mut ptr = start.add(index);
1029+
let ptr = start.add(index);
10271030

10281031
// Move the trailing elements.
10291032
ptr::copy(ptr, ptr.add(lower_size_bound), old_len - index);
@@ -1036,42 +1039,39 @@ impl<A: Array> SmallVec<A> {
10361039
len: old_len + lower_size_bound,
10371040
};
10381041

1039-
let mut num_added = 0;
1040-
for element in iter {
1041-
let mut cur = ptr.add(num_added);
1042-
if num_added >= lower_size_bound {
1043-
// Iterator provided more elements than the hint. Move trailing items again.
1044-
self.reserve(1);
1045-
let start = self.as_mut_ptr();
1046-
ptr = start.add(index);
1047-
cur = ptr.add(num_added);
1048-
ptr::copy(cur, cur.add(1), old_len - index);
1049-
1050-
guard.start = start;
1051-
guard.len += 1;
1052-
guard.skip.end += 1;
1053-
}
1042+
while num_added < lower_size_bound {
1043+
let element = match iter.next() {
1044+
Some(x) => x,
1045+
None => break,
1046+
};
1047+
let cur = ptr.add(num_added);
10541048
ptr::write(cur, element);
10551049
guard.skip.start += 1;
10561050
num_added += 1;
10571051
}
1058-
mem::forget(guard);
10591052

10601053
if num_added < lower_size_bound {
1061-
// Iterator provided fewer elements than the hint
1054+
// Iterator provided fewer elements than the hint. Move the tail backward.
10621055
ptr::copy(
10631056
ptr.add(lower_size_bound),
10641057
ptr.add(num_added),
10651058
old_len - index,
10661059
);
10671060
}
1068-
1061+
// There are no more duplicate or uninitialized slots, so the guard is not needed.
10691062
self.set_len(old_len + num_added);
1063+
mem::forget(guard);
1064+
}
1065+
1066+
// Insert any remaining elements one-by-one.
1067+
for element in iter {
1068+
self.insert(index + num_added, element);
1069+
num_added += 1;
10701070
}
10711071

10721072
struct DropOnPanic<T> {
10731073
start: *mut T,
1074-
skip: Range<usize>,
1074+
skip: Range<usize>, // Space we copied-out-of, but haven't written-to yet.
10751075
len: usize,
10761076
}
10771077

src/tests.rs

+13
Original file line numberDiff line numberDiff line change
@@ -905,3 +905,16 @@ fn empty_macro() {
905905
fn zero_size_items() {
906906
SmallVec::<[(); 0]>::new().push(());
907907
}
908+
909+
#[test]
910+
fn test_insert_many_overflow() {
911+
let mut v: SmallVec<[u8; 1]> = SmallVec::new();
912+
v.push(123);
913+
914+
// Prepare an iterator with small lower bound
915+
let iter = (0u8..5).filter(|n| n % 2 == 0);
916+
assert_eq!(iter.size_hint().0, 0);
917+
918+
v.insert_many(0, iter);
919+
assert_eq!(&*v, &[0, 2, 4, 123]);
920+
}

0 commit comments

Comments
 (0)