Skip to content

Commit

Permalink
Clamp Vec::with_capacity to 64KiB to avoid OOM
Browse files Browse the repository at this point in the history
  • Loading branch information
jkugelman authored and Geal committed Sep 12, 2022
1 parent b4aeb3b commit 3645656
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 2 deletions.
15 changes: 13 additions & 2 deletions src/multi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ use crate::lib::std::vec::Vec;
use crate::traits::{InputLength, InputTake, ToUsize};
use core::num::NonZeroUsize;

/// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`.
///
/// Pre-allocating memory is a nice optimization but count fields can't
/// always be trusted. We should clamp initial capacities to some reasonable
/// amount. This reduces the risk of a bogus count value triggering a panic
/// due to an OOM error.
///
/// This does not affect correctness. Nom will always read the full number
/// of elements regardless of the capacity cap.
const MAX_INITIAL_CAPACITY: usize = 65536;

/// Repeats the embedded parser until it fails
/// and returns the results in a `Vec`.
///
Expand Down Expand Up @@ -362,7 +373,7 @@ where
return Err(Err::Failure(E::from_error_kind(input, ErrorKind::ManyMN)));
}

let mut res = crate::lib::std::vec::Vec::with_capacity(min);
let mut res = crate::lib::std::vec::Vec::with_capacity(min.clamp(0, MAX_INITIAL_CAPACITY));
for count in 0..max {
let len = input.input_len();
match parse.parse(input.clone()) {
Expand Down Expand Up @@ -529,7 +540,7 @@ where
{
move |i: I| {
let mut input = i.clone();
let mut res = crate::lib::std::vec::Vec::with_capacity(count);
let mut res = crate::lib::std::vec::Vec::with_capacity(count.clamp(0, MAX_INITIAL_CAPACITY));

for _ in 0..count {
let input_ = input.clone();
Expand Down
15 changes: 15 additions & 0 deletions tests/issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,18 @@ fn issue_1282_findtoken_char() {
let parser = one_of::<_, _, Error<_>>(&['a', 'b', 'c'][..]);
assert_eq!(parser("aaa"), Ok(("aa", 'a')));
}

#[test]
fn issue_1459_clamp_capacity() {
use nom::character::complete::char;

// shouldn't panic
use nom::multi::many_m_n;
let mut parser = many_m_n::<_, _, (), _>(usize::MAX, usize::MAX, char('a'));
assert_eq!(parser("a"), Err(nom::Err::Error(())));

// shouldn't panic
use nom::multi::count;
let mut parser = count::<_, _, (), _>(char('a'), usize::MAX);
assert_eq!(parser("a"), Err(nom::Err::Error(())));
}

0 comments on commit 3645656

Please sign in to comment.