From b19e69d5a1d3c363f98f9994fbf3d5a2a6c0ceb8 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 6 Dec 2021 13:29:50 +0100 Subject: [PATCH] Limit line length to 100 MB to not exhaust system memory on `/dev/zero` Closes #636 --- CHANGELOG.md | 1 + src/input.rs | 16 ++++++++++++++-- tests/integration_tests.rs | 12 ++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7971bb4ed..deb4f8b0b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Python syntax highlighting no longer suffers from abysmal performance in specific scenarios. See #1688 (@keith-hall) - First line not shown in diff context. See #1891 (@divagant-martian) - Do not ignore syntaxes that handle file names with a `*.conf` extension. See #1703 (@cbolgiano) +- Limit line length to 100 MB to not exhaust system memory on `/dev/zero`. See #1972 (@Enselic) ## Performance diff --git a/src/input.rs b/src/input.rs index ffaca0ae0b..d8123ff0bd 100644 --- a/src/input.rs +++ b/src/input.rs @@ -251,7 +251,7 @@ pub(crate) struct InputReader<'a> { impl<'a> InputReader<'a> { fn new(mut reader: R) -> InputReader<'a> { let mut first_line = vec![]; - reader.read_until(b'\n', &mut first_line).ok(); + Self::read_line_safely(&mut reader, &mut first_line).ok(); let content_type = if first_line.is_empty() { None @@ -276,7 +276,7 @@ impl<'a> InputReader<'a> { return Ok(true); } - let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?; + let res = Self::read_line_safely(&mut self.inner, buf).map(|size| size > 0)?; if self.content_type == Some(ContentType::UTF_16LE) { let _ = self.inner.read_until(0x00, buf); @@ -284,6 +284,18 @@ impl<'a> InputReader<'a> { Ok(res) } + + fn read_line_safely(reader: &mut R, buf: &mut Vec) -> io::Result { + let limit = 100_000_000_usize; + + match reader.take(limit as u64).read_until(b'\n', buf) { + Ok(n) if n == limit => Err(io::Error::new( + io::ErrorKind::Other, + "Lines longer than 100 MB are not supported", + )), + r => r, + } + } } #[test] diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 17e9dc4e1f..86cb347d8c 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -344,6 +344,18 @@ fn no_args_doesnt_break() { assert!(exit_status.success()); } +#[cfg(unix)] +#[test] +fn dev_zero() { + bat() + .arg("/dev/zero") + .assert() + .failure() + .stderr(predicate::str::contains( + "Lines longer than 100 MB are not supported", + )); +} + #[test] fn tabs_numbers() { bat()