Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clamp Vec::with_capacity to 64KiB to avoid OOM #1545

Merged
merged 1 commit into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(())));
}