Merge pull request #8435 from julian-klode/tr-enomem

tr: fix high memory use, possible heap exhaustion
This commit is contained in:
Sylvestre Ledru 2025-08-06 10:00:19 +02:00 committed by GitHub
commit 76bb429d19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 13 deletions

View file

@ -672,15 +672,17 @@ where
R: BufRead,
W: Write,
{
let mut buf = Vec::new();
let mut buf = [0; 8192];
let mut output_buf = Vec::new();
while let Ok(length) = input.read_until(b'\n', &mut buf) {
while let Ok(length) = input.read(&mut buf[..]) {
if length == 0 {
break; // EOF reached
}
let filtered = buf.iter().filter_map(|&c| translator.translate(c));
let filtered = buf[..length]
.iter()
.filter_map(|&c| translator.translate(c));
output_buf.extend(filtered);
#[cfg(not(target_os = "windows"))]
@ -698,7 +700,6 @@ where
}
}
buf.clear();
output_buf.clear();
}

View file

@ -12,7 +12,7 @@ use operation::{
translate_input,
};
use std::ffi::OsString;
use std::io::{BufWriter, Write, stdin, stdout};
use std::io::{Write, stdin, stdout};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::fs::is_stdin_directory;
@ -107,7 +107,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let stdin = stdin();
let mut locked_stdin = stdin.lock();
let mut buffered_stdout = BufWriter::new(stdout().lock());
let mut locked_stdout = stdout().lock();
// According to the man page: translating only happens if deleting or if a second set is given
let translating = !delete_flag && sets.len() > 1;
@ -131,34 +131,34 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let delete_op = DeleteOperation::new(set1);
let squeeze_op = SqueezeOperation::new(set2);
let op = delete_op.chain(squeeze_op);
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
translate_input(&mut locked_stdin, &mut locked_stdout, op)?;
} else {
let op = DeleteOperation::new(set1);
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
translate_input(&mut locked_stdin, &mut locked_stdout, op)?;
}
} else if squeeze_flag {
if sets_len == 1 {
let op = SqueezeOperation::new(set1);
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
translate_input(&mut locked_stdin, &mut locked_stdout, op)?;
} else {
let translate_op = TranslateOperation::new(set1, set2.clone())?;
let squeeze_op = SqueezeOperation::new(set2);
let op = translate_op.chain(squeeze_op);
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
translate_input(&mut locked_stdin, &mut locked_stdout, op)?;
}
} else {
let op = TranslateOperation::new(set1, set2)?;
translate_input(&mut locked_stdin, &mut buffered_stdout, op)?;
translate_input(&mut locked_stdin, &mut locked_stdout, op)?;
}
#[cfg(not(target_os = "windows"))]
buffered_stdout
locked_stdout
.flush()
.map_err_context(|| translate!("tr-error-write-error"))?;
// SIGPIPE is not available on Windows.
#[cfg(target_os = "windows")]
match buffered_stdout.flush() {
match locked_stdout.flush() {
Ok(()) => {}
Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => std::process::exit(13),
Err(err) => return Err(err.map_err_context(|| translate!("tr-error-write-error"))),