fix: avoid closing pty master fd twice

The pty master fd is closed twice, once when dropping OwnedFd in `fn exec` and
once when dropping File constructed with unsafe code in `fn copy`.

This causes IO Safety violation[1] and should be avoided.
It becomes a crash in debug build[2].

[1]: 1ba00d9cb2
[2]: https://github.com/zed-industries/zed/issues/12114
This commit is contained in:
Levi Zim
2024-10-05 13:40:01 +08:00
parent 2728a10bbd
commit 848ab8e744

View File

@@ -10,12 +10,13 @@ use nix::{libc, pty};
use signal_hook::consts::{SIGALRM, SIGCHLD, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGWINCH}; use signal_hook::consts::{SIGALRM, SIGCHLD, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGWINCH};
use signal_hook::SigId; use signal_hook::SigId;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::ffi::{CString, NulError}; use std::ffi::{CString, NulError};
use std::fs::File;
use std::io::{self, ErrorKind, Read, Write}; use std::io::{self, ErrorKind, Read, Write};
use std::os::fd::{AsFd, RawFd}; use std::os::fd::AsFd;
use std::os::fd::{BorrowedFd, OwnedFd}; use std::os::fd::{BorrowedFd, OwnedFd};
use std::os::unix::io::{AsRawFd, FromRawFd}; use std::os::unix::io::{AsRawFd, FromRawFd};
use std::{env, fs};
type ExtraEnv = HashMap<String, String>; type ExtraEnv = HashMap<String, String>;
@@ -37,9 +38,7 @@ pub fn exec<S: AsRef<str>, T: Tty + ?Sized, H: Handler>(
let result = unsafe { pty::forkpty(Some(&winsize), None) }?; let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
match result.fork_result { match result.fork_result {
ForkResult::Parent { child } => { ForkResult::Parent { child } => handle_parent(result.master, child, tty, handler),
handle_parent(result.master.as_raw_fd(), child, tty, handler)
}
ForkResult::Child => { ForkResult::Child => {
handle_child(command, extra_env)?; handle_child(command, extra_env)?;
@@ -49,7 +48,7 @@ pub fn exec<S: AsRef<str>, T: Tty + ?Sized, H: Handler>(
} }
fn handle_parent<T: Tty + ?Sized, H: Handler>( fn handle_parent<T: Tty + ?Sized, H: Handler>(
master_fd: RawFd, master_fd: OwnedFd,
child: unistd::Pid, child: unistd::Pid,
tty: &mut T, tty: &mut T,
handler: &mut H, handler: &mut H,
@@ -75,12 +74,13 @@ fn handle_parent<T: Tty + ?Sized, H: Handler>(
const BUF_SIZE: usize = 128 * 1024; const BUF_SIZE: usize = 128 * 1024;
fn copy<T: Tty + ?Sized, H: Handler>( fn copy<T: Tty + ?Sized, H: Handler>(
master_raw_fd: RawFd, master_fd: OwnedFd,
child: unistd::Pid, child: unistd::Pid,
tty: &mut T, tty: &mut T,
handler: &mut H, handler: &mut H,
) -> Result<Option<WaitStatus>> { ) -> Result<Option<WaitStatus>> {
let mut master = unsafe { fs::File::from_raw_fd(master_raw_fd) }; let mut master = File::from(master_fd);
let master_raw_fd = master.as_raw_fd();
let mut buf = [0u8; BUF_SIZE]; let mut buf = [0u8; BUF_SIZE];
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE); let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE); let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);