Skip to content

Commit 1a67970

Browse files
committed
feat: Make Client::from_env safe to call for any number of time
So that it can be used in different crates without causing UB. Signed-off-by: Jiahao XU <[email protected]>
1 parent 35a2d2e commit 1a67970

File tree

2 files changed

+26
-8
lines changed

2 files changed

+26
-8
lines changed

src/lib.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,7 @@ impl Client {
256256
/// make sure to take ownership properly of the file descriptors passed
257257
/// down, if any.
258258
///
259-
/// It's generally unsafe to call this function twice in a program if the
260-
/// previous invocation returned `Some`.
261-
///
262-
/// Note, though, that on Windows it should be safe to call this function
263-
/// any number of times.
259+
/// It is ok to call this function any number of times.
264260
pub unsafe fn from_env_ext(check_pipe: bool) -> FromEnv {
265261
let (env, var_os) = match ["CARGO_MAKEFLAGS", "MAKEFLAGS", "MFLAGS"]
266262
.iter()
@@ -299,6 +295,19 @@ impl Client {
299295
/// environment.
300296
///
301297
/// Wraps `from_env_ext` and discards error details.
298+
///
299+
/// # Safety
300+
///
301+
/// This function is `unsafe` to call on Unix specifically as it
302+
/// transitively requires usage of the `from_raw_fd` function, which is
303+
/// itself unsafe in some circumstances.
304+
///
305+
/// It's recommended to call this function very early in the lifetime of a
306+
/// program before any other file descriptors are opened. That way you can
307+
/// make sure to take ownership properly of the file descriptors passed
308+
/// down, if any.
309+
///
310+
/// It is ok to call this function any number of times.
302311
pub unsafe fn from_env() -> Option<Client> {
303312
Self::from_env_ext(false).client.ok()
304313
}

src/unix.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,10 @@ impl Client {
149149
}
150150
}
151151

152-
drop(set_cloexec(read, true));
153-
drop(set_cloexec(write, true));
154-
Ok(Some(Client::from_fds(read, write)))
152+
Ok(Some(Client::Pipe {
153+
read: clone_fd_and_set_cloexec(read)?,
154+
write: clone_fd_and_set_cloexec(write)?,
155+
}))
155156
}
156157

157158
unsafe fn from_fds(read: c_int, write: c_int) -> Client {
@@ -435,6 +436,14 @@ unsafe fn fd_check(fd: c_int, check_pipe: bool) -> Result<(), FromEnvErrorInner>
435436
}
436437
}
437438

439+
fn clone_fd_and_set_cloexec(fd: c_int) -> Result<File, FromEnvErrorInner> {
440+
// Safety: File is wrapped in `ManuallyDrop` to prevent closing on drop
441+
// since we don't own the fd.
442+
mem::ManuallyDrop::new(unsafe { File::from_raw_fd(fd) })
443+
.try_clone()
444+
.map_err(|err| FromEnvErrorInner::CannotOpenFd(fd, err))
445+
}
446+
438447
fn set_cloexec(fd: c_int, set: bool) -> io::Result<()> {
439448
unsafe {
440449
let previous = cvt(libc::fcntl(fd, libc::F_GETFD))?;

0 commit comments

Comments
 (0)