Skip to content

Commit 3645656

Browse files
jkugelmanGeal
authored andcommitted
Clamp Vec::with_capacity to 64KiB to avoid OOM
1 parent b4aeb3b commit 3645656

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

src/multi/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ use crate::lib::std::vec::Vec;
1111
use crate::traits::{InputLength, InputTake, ToUsize};
1212
use core::num::NonZeroUsize;
1313

14+
/// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`.
15+
///
16+
/// Pre-allocating memory is a nice optimization but count fields can't
17+
/// always be trusted. We should clamp initial capacities to some reasonable
18+
/// amount. This reduces the risk of a bogus count value triggering a panic
19+
/// due to an OOM error.
20+
///
21+
/// This does not affect correctness. Nom will always read the full number
22+
/// of elements regardless of the capacity cap.
23+
const MAX_INITIAL_CAPACITY: usize = 65536;
24+
1425
/// Repeats the embedded parser until it fails
1526
/// and returns the results in a `Vec`.
1627
///
@@ -362,7 +373,7 @@ where
362373
return Err(Err::Failure(E::from_error_kind(input, ErrorKind::ManyMN)));
363374
}
364375

365-
let mut res = crate::lib::std::vec::Vec::with_capacity(min);
376+
let mut res = crate::lib::std::vec::Vec::with_capacity(min.clamp(0, MAX_INITIAL_CAPACITY));
366377
for count in 0..max {
367378
let len = input.input_len();
368379
match parse.parse(input.clone()) {
@@ -529,7 +540,7 @@ where
529540
{
530541
move |i: I| {
531542
let mut input = i.clone();
532-
let mut res = crate::lib::std::vec::Vec::with_capacity(count);
543+
let mut res = crate::lib::std::vec::Vec::with_capacity(count.clamp(0, MAX_INITIAL_CAPACITY));
533544

534545
for _ in 0..count {
535546
let input_ = input.clone();

tests/issues.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,18 @@ fn issue_1282_findtoken_char() {
214214
let parser = one_of::<_, _, Error<_>>(&['a', 'b', 'c'][..]);
215215
assert_eq!(parser("aaa"), Ok(("aa", 'a')));
216216
}
217+
218+
#[test]
219+
fn issue_1459_clamp_capacity() {
220+
use nom::character::complete::char;
221+
222+
// shouldn't panic
223+
use nom::multi::many_m_n;
224+
let mut parser = many_m_n::<_, _, (), _>(usize::MAX, usize::MAX, char('a'));
225+
assert_eq!(parser("a"), Err(nom::Err::Error(())));
226+
227+
// shouldn't panic
228+
use nom::multi::count;
229+
let mut parser = count::<_, _, (), _>(char('a'), usize::MAX);
230+
assert_eq!(parser("a"), Err(nom::Err::Error(())));
231+
}

0 commit comments

Comments
 (0)