diff --git a/src/uu/cat/src/splice.rs b/src/uu/cat/src/splice.rs index 0b46fc662..108997c4a 100644 --- a/src/uu/cat/src/splice.rs +++ b/src/uu/cat/src/splice.rs @@ -2,8 +2,9 @@ use super::{CatResult, InputHandle}; use nix::fcntl::{splice, SpliceFFlags}; use nix::unistd::{self, pipe}; +use std::fs::File; use std::io::Read; -use std::os::unix::io::RawFd; +use std::os::unix::io::{FromRawFd, RawFd}; const BUF_SIZE: usize = 1024 * 16; @@ -19,13 +20,11 @@ pub(super) fn write_fast_using_splice( handle: &mut InputHandle, write_fd: RawFd, ) -> CatResult { - let (pipe_rd, pipe_wr) = match pipe() { - Ok(r) => r, - Err(_) => { - // It is very rare that creating a pipe fails, but it can happen. - return Ok(true); - } - }; + let (pipe_rd, pipe_wr) = pipe()?; + + // Ensure the pipe is closed when the function returns. + // SAFETY: The file descriptors do not have other owners. + let _handles = unsafe { (File::from_raw_fd(pipe_rd), File::from_raw_fd(pipe_wr)) }; loop { match splice( diff --git a/src/uu/wc/src/count_bytes.rs b/src/uu/wc/src/count_bytes.rs index 7f06f8171..83cc71ac4 100644 --- a/src/uu/wc/src/count_bytes.rs +++ b/src/uu/wc/src/count_bytes.rs @@ -1,7 +1,7 @@ use super::{WcResult, WordCountable}; #[cfg(any(target_os = "linux", target_os = "android"))] -use std::fs::OpenOptions; +use std::fs::{File, OpenOptions}; use std::io::ErrorKind; #[cfg(unix)] @@ -9,7 +9,7 @@ use libc::S_IFREG; #[cfg(unix)] use nix::sys::stat::fstat; #[cfg(any(target_os = "linux", target_os = "android"))] -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; #[cfg(any(target_os = "linux", target_os = "android"))] use libc::S_IFIFO; @@ -47,6 +47,10 @@ fn count_bytes_using_splice(fd: RawFd) -> nix::Result { let null = null_file.as_raw_fd(); let (pipe_rd, pipe_wr) = pipe()?; + // Ensure the pipe is closed when the function returns. + // SAFETY: The file descriptors do not have other owners. + let _handles = unsafe { (File::from_raw_fd(pipe_rd), File::from_raw_fd(pipe_wr)) }; + let mut byte_count = 0; loop { let res = splice(fd, None, pipe_wr, None, BUF_SIZE, SpliceFFlags::empty())?; diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index e52be9506..b629a06e6 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -1,8 +1,13 @@ +// spell-checker:ignore NOFILE + use crate::common::util::*; use std::fs::OpenOptions; #[cfg(unix)] use std::io::Read; +#[cfg(target_os = "linux")] +use rlimit::Resource; + #[test] fn test_output_simple() { new_ucmd!() @@ -87,6 +92,23 @@ fn test_fifo_symlink() { thread.join().unwrap(); } +#[test] +#[cfg(target_os = "linux")] +fn test_closes_file_descriptors() { + // Each file creates a pipe, which has two file descriptors. + // If they are not closed then five is certainly too many. + new_ucmd!() + .args(&[ + "alpha.txt", + "alpha.txt", + "alpha.txt", + "alpha.txt", + "alpha.txt", + ]) + .with_limit(Resource::NOFILE, 9, 9) + .succeeds(); +} + #[test] #[cfg(unix)] fn test_piped_to_regular_file() {