1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

Close file descriptors of pipes after use (#2591)

* Close file descriptors of pipes after use

* cat: Test file descriptor exhaustion

* fixup! cat: Test file descriptor exhaustion
This commit is contained in:
Jan Verbeek 2021-08-24 12:00:07 +02:00 committed by GitHub
parent c77115ab51
commit 516c5311f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 10 deletions

View file

@ -2,8 +2,9 @@ use super::{CatResult, InputHandle};
use nix::fcntl::{splice, SpliceFFlags}; use nix::fcntl::{splice, SpliceFFlags};
use nix::unistd::{self, pipe}; use nix::unistd::{self, pipe};
use std::fs::File;
use std::io::Read; use std::io::Read;
use std::os::unix::io::RawFd; use std::os::unix::io::{FromRawFd, RawFd};
const BUF_SIZE: usize = 1024 * 16; const BUF_SIZE: usize = 1024 * 16;
@ -19,13 +20,11 @@ pub(super) fn write_fast_using_splice<R: Read>(
handle: &mut InputHandle<R>, handle: &mut InputHandle<R>,
write_fd: RawFd, write_fd: RawFd,
) -> CatResult<bool> { ) -> CatResult<bool> {
let (pipe_rd, pipe_wr) = match pipe() { let (pipe_rd, pipe_wr) = pipe()?;
Ok(r) => r,
Err(_) => { // Ensure the pipe is closed when the function returns.
// It is very rare that creating a pipe fails, but it can happen. // SAFETY: The file descriptors do not have other owners.
return Ok(true); let _handles = unsafe { (File::from_raw_fd(pipe_rd), File::from_raw_fd(pipe_wr)) };
}
};
loop { loop {
match splice( match splice(

View file

@ -1,7 +1,7 @@
use super::{WcResult, WordCountable}; use super::{WcResult, WordCountable};
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use std::fs::OpenOptions; use std::fs::{File, OpenOptions};
use std::io::ErrorKind; use std::io::ErrorKind;
#[cfg(unix)] #[cfg(unix)]
@ -9,7 +9,7 @@ use libc::S_IFREG;
#[cfg(unix)] #[cfg(unix)]
use nix::sys::stat::fstat; use nix::sys::stat::fstat;
#[cfg(any(target_os = "linux", target_os = "android"))] #[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"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use libc::S_IFIFO; use libc::S_IFIFO;
@ -47,6 +47,10 @@ fn count_bytes_using_splice(fd: RawFd) -> nix::Result<usize> {
let null = null_file.as_raw_fd(); let null = null_file.as_raw_fd();
let (pipe_rd, pipe_wr) = pipe()?; 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; let mut byte_count = 0;
loop { loop {
let res = splice(fd, None, pipe_wr, None, BUF_SIZE, SpliceFFlags::empty())?; let res = splice(fd, None, pipe_wr, None, BUF_SIZE, SpliceFFlags::empty())?;

View file

@ -1,8 +1,13 @@
// spell-checker:ignore NOFILE
use crate::common::util::*; use crate::common::util::*;
use std::fs::OpenOptions; use std::fs::OpenOptions;
#[cfg(unix)] #[cfg(unix)]
use std::io::Read; use std::io::Read;
#[cfg(target_os = "linux")]
use rlimit::Resource;
#[test] #[test]
fn test_output_simple() { fn test_output_simple() {
new_ucmd!() new_ucmd!()
@ -87,6 +92,23 @@ fn test_fifo_symlink() {
thread.join().unwrap(); 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] #[test]
#[cfg(unix)] #[cfg(unix)]
fn test_piped_to_regular_file() { fn test_piped_to_regular_file() {