Skip to content

Commit cb2c70d

Browse files
authored
Fix dup2_stdout et al to handle fd aliasing. (#1069)
* Fix `dup2_stdout` et al to handle fd aliasing. Fix `dup2_stdout` et al to work in the case where the file descriptor they are passed is the same as the file descriptor they implicitly operate on, including on targets that don't have a `dup2` syscall and implement it using `dup3`, such as aarch64. Fixes #1067. * rustfmt and fix test compilation.
1 parent c204605 commit cb2c70d

File tree

3 files changed

+52
-16
lines changed

3 files changed

+52
-16
lines changed

src/stdio.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ use backend::c;
1414
use backend::fd::{BorrowedFd, FromRawFd, RawFd};
1515

1616
#[cfg(not(any(windows, target_os = "wasi")))]
17-
use {crate::io, backend::fd::AsFd, core::mem::forget};
17+
use {
18+
crate::io,
19+
backend::fd::{AsFd, AsRawFd},
20+
core::mem::forget,
21+
};
1822

1923
/// `STDIN_FILENO`—Standard input, borrowed.
2024
///
@@ -476,11 +480,13 @@ pub const fn raw_stderr() -> RawFd {
476480
#[allow(clippy::mem_forget)]
477481
#[inline]
478482
pub fn dup2_stdin<Fd: AsFd>(fd: Fd) -> io::Result<()> {
479-
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
480-
// dropped.
481-
let mut target = unsafe { take_stdin() };
482-
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
483-
forget(target);
483+
if fd.as_fd().as_raw_fd() != c::STDIN_FILENO {
484+
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
485+
// dropped.
486+
let mut target = unsafe { take_stdin() };
487+
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
488+
forget(target);
489+
}
484490
Ok(())
485491
}
486492

@@ -489,11 +495,13 @@ pub fn dup2_stdin<Fd: AsFd>(fd: Fd) -> io::Result<()> {
489495
#[allow(clippy::mem_forget)]
490496
#[inline]
491497
pub fn dup2_stdout<Fd: AsFd>(fd: Fd) -> io::Result<()> {
492-
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
493-
// dropped.
494-
let mut target = unsafe { take_stdout() };
495-
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
496-
forget(target);
498+
if fd.as_fd().as_raw_fd() != c::STDOUT_FILENO {
499+
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
500+
// dropped.
501+
let mut target = unsafe { take_stdout() };
502+
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
503+
forget(target);
504+
}
497505
Ok(())
498506
}
499507

@@ -502,10 +510,12 @@ pub fn dup2_stdout<Fd: AsFd>(fd: Fd) -> io::Result<()> {
502510
#[allow(clippy::mem_forget)]
503511
#[inline]
504512
pub fn dup2_stderr<Fd: AsFd>(fd: Fd) -> io::Result<()> {
505-
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
506-
// dropped.
507-
let mut target = unsafe { take_stderr() };
508-
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
509-
forget(target);
513+
if fd.as_fd().as_raw_fd() != c::STDERR_FILENO {
514+
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
515+
// dropped.
516+
let mut target = unsafe { take_stderr() };
517+
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
518+
forget(target);
519+
}
510520
Ok(())
511521
}

tests/stdio/dup2_stdio.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use rustix::io::fcntl_getfd;
2+
use rustix::stdio::{dup2_stdout, stdout};
3+
4+
#[test]
5+
fn dup2_stdin_stdin() {
6+
let _ = dup2_stdout(stdout());
7+
fcntl_getfd(stdout()).unwrap();
8+
}
9+
10+
#[test]
11+
fn dup2_stdout_stdout() {
12+
let _ = dup2_stdout(stdout());
13+
fcntl_getfd(stdout()).unwrap();
14+
}
15+
16+
#[test]
17+
fn dup2_stderr_stderr() {
18+
let _ = dup2_stdout(stdout());
19+
fcntl_getfd(stdout()).unwrap();
20+
}

tests/stdio/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
//! Tests for [`rustix::stdio`].
22
3+
#![cfg(feature = "stdio")]
4+
5+
#[cfg(not(feature = "rustc-dep-of-std"))]
6+
#[cfg(not(windows))]
7+
#[cfg(not(target_os = "wasi"))]
8+
mod dup2_stdio;
39
#[cfg(not(feature = "rustc-dep-of-std"))]
410
#[cfg(not(windows))]
511
#[cfg(not(target_os = "wasi"))]

0 commit comments

Comments
 (0)