From b609db90ef515230f48203a3c76175ead1caa334 Mon Sep 17 00:00:00 2001 From: easonysliu Date: Sat, 14 Mar 2026 20:41:16 +0800 Subject: [PATCH] tac: avoid mmap on regular files to prevent SIGBUS on truncation When a file is memory-mapped and then truncated by another process while tac is reading it, accessing the now-invalid mapped region triggers SIGBUS and crashes the process. This is particularly problematic for log rotation scenarios where tac might read logs that are being rotated/truncated concurrently. Replace the mmap-based file reading with read_to_end(), which reads the entire file into a heap buffer. This matches how GNU tac avoids the issue. The mmap path is retained for stdin (via temp files we own), where external truncation is not a concern. Fixes #9748 Co-Authored-By: Claude (claude-opus-4-6) --- src/uu/tac/src/tac.rs | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 88c355b0e4b..78195e27608 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -383,20 +383,19 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &OsStr) -> } }; - if let Some(mmap1) = try_mmap_file(&file) { - mmap = mmap1; - &mmap - } else { - let mut contents = Vec::new(); - match file.read_to_end(&mut contents) { - Ok(_) => { - buf = contents; - &buf - } - Err(e) => { - show!(TacError::ReadError(filename.clone(), e)); - continue; - } + // Read the file into memory instead of memory-mapping it. + // Using mmap on regular files is unsafe because if the file is + // truncated while mapped, accessing the now-invalid region + // triggers SIGBUS and crashes the process (see #9748). + let mut contents = Vec::new(); + match file.read_to_end(&mut contents) { + Ok(_) => { + buf = contents; + &buf + } + Err(e) => { + show!(TacError::ReadError(filename.clone(), e)); + continue; } } }; @@ -450,11 +449,6 @@ fn buffer_stdin() -> std::io::Result { } } -fn try_mmap_file(file: &File) -> Option { - // SAFETY: If the file is truncated while we map it, SIGBUS will be raised - // and our process will be terminated, thus preventing access of invalid memory. - unsafe { Mmap::map(file).ok() } -} #[cfg(test)] mod tests_hybrid_flavor {