Skip to content

Commit 59213cd

Browse files
bors[bot]kosayoda
andauthored
Merge #97
97: Prevent file descriptor leak in the spawned child. r=matthiasbeyer a=kosayoda ## Problem After [forking the child process](https://github.com/rust-cli/rexpect/blob/cee87fc7a2ef743f09e1e880c8cf7b268a59b351/src/process.rs#L98), the file descriptors for the master and slave aren't closed since we created these descriptors ourselves (Rust sets CLOEXEC on any fds created in the stdlib). I stumbled upon the problem when my child became a orphan after sending SIGINT to its parent. Since the drop handlers weren't run, the child isn't terminated/waited on. The expected scenario (what pexpect does) is that the master side of the pty is closed by the kernel on process exit, and SIGHUP is sent to the child process (which kills the child by default). However, since the controlling terminal is left open by the child, SIGHUP is not sent. ## Reproduction and Fix ```rust use rexpect::spawn; fn main() { let mut p = spawn("sleep 100", Some(30_000)).unwrap(); // Hang parent let mut s = String::new(); std::io::stdin().read_line(&mut s).unwrap(); } ``` On the current master: <img width="375" alt="image" src="https://user-images.githubusercontent.com/41782385/229242068-01c6b68e-30b4-4c3b-a99d-62ed513fd9e9.png"> On the PR branch: <img width="372" alt="image" src="https://user-images.githubusercontent.com/41782385/229242179-98e6085e-a671-42d2-93fc-9075a2ba72b4.png"> Co-authored-by: kosayoda <[email protected]>
2 parents cee87fc + 10e68bf commit 59213cd

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

src/process.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
77
use nix::pty::{grantpt, posix_openpt, unlockpt, PtyMaster};
88
pub use nix::sys::{signal, wait};
99
use nix::sys::{stat, termios};
10-
use nix::unistd::{dup, dup2, fork, setsid, ForkResult, Pid};
10+
use nix::unistd::{close, dup, dup2, fork, setsid, ForkResult, Pid};
1111
use std;
1212
use std::fs::File;
1313
use std::os::unix::io::{AsRawFd, FromRawFd};
@@ -97,6 +97,9 @@ impl PtyProcess {
9797

9898
match unsafe { fork()? } {
9999
ForkResult::Child => {
100+
// Avoid leaking master fd
101+
close(master_fd.as_raw_fd())?;
102+
100103
setsid()?; // create new session with child as session leader
101104
let slave_fd = open(
102105
std::path::Path::new(&slave_name),
@@ -109,6 +112,11 @@ impl PtyProcess {
109112
dup2(slave_fd, STDOUT_FILENO)?;
110113
dup2(slave_fd, STDERR_FILENO)?;
111114

115+
// Avoid leaking slave fd
116+
if slave_fd > STDERR_FILENO {
117+
close(slave_fd)?;
118+
}
119+
112120
// set echo off
113121
let mut flags = termios::tcgetattr(STDIN_FILENO)?;
114122
flags.local_flags &= !termios::LocalFlags::ECHO;

0 commit comments

Comments
 (0)