Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libstd: unix process spawning: fix bug with setting stdio #30490

Merged
merged 1 commit into from
Jan 11, 2016
Merged

libstd: unix process spawning: fix bug with setting stdio #30490

merged 1 commit into from
Jan 11, 2016

Conversation

ipetkov
Copy link
Contributor

@ipetkov ipetkov commented Dec 20, 2015

  • If the requested descriptors to inherit are stdio descriptors there
    are situations where they will not be set correctly
  • Example: parent's stdout --> child's stderr
    parent's stderr --> child's stdout
  • Solution: if the requested descriptors for the child are stdio
    descriptors, dup them before overwriting the child's stdio

Example of a program which exhibits the bug:

// stdio.rs
use std::io::Write;
use std::io::{stdout, stderr};
use std::process::{Command, Stdio};
use std::os::unix::io::FromRawFd;

fn main() {
    stdout().write_all("parent stdout\n".as_bytes()).unwrap();
    stderr().write_all("parent stderr\n".as_bytes()).unwrap();

    Command::new("sh")
        .arg("-c")
        .arg("echo 'child stdout'; echo 'child stderr' 1>&2")
        .stdin(Stdio::inherit())
        .stdout(unsafe { FromRawFd::from_raw_fd(2) })
        .stderr(unsafe { FromRawFd::from_raw_fd(1) })
        .status()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
}

Before:

$ rustc --version
rustc 1.7.0-nightly (8ad12c3e2 2015-12-19)
$ rustc stdio.rs && ./stdio >out 2>err
$ cat out
parent stdout
$ cat err
parent stderr
child stdout
child stderr

After (expected):

$ rustc --version
rustc 1.7.0-dev (712eccee2 2015-12-19)
$ rustc stdio.rs && ./stdio >out 2>err
$ cat out
parent stdout
child stderr
$ cat err
parent stderr
child stdout

@rust-highfive
Copy link
Collaborator

r? @brson

(rust_highfive has picked a reviewer for you, use r? to override)

@ipetkov
Copy link
Contributor Author

ipetkov commented Dec 20, 2015

r? @alexcrichton

@rust-highfive rust-highfive assigned alexcrichton and unassigned brson Dec 20, 2015
Stdio::Raw(fd@libc::STDERR_FILENO) => match cvt_r(|| libc::dup(fd)) {
Err(_) => fail(output),
Ok(fd) => {
let fd = RawStdio::new(fd);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use FileDesc instead of RawStdio here? (as RawStdio is just a type alias)

@alexcrichton
Copy link
Member

Thanks! Could you be sure to also add a test for this?

@ipetkov
Copy link
Contributor Author

ipetkov commented Dec 22, 2015

@alexcrichton addressed all comments! I wasn't sure about the right place for such a test, but I think run-pass is a good fit.


{
let stdout = File::create(&stdout_file_path).expect("failed to create stdout file");
let stderr = File::create(&stderr_file_path).expect("failed to create stderr file");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this test be written without creating files? Keeping everything in-memory is generally better for isolation. I think this should basically just be able to use combinations of pipes, right?

@ipetkov
Copy link
Contributor Author

ipetkov commented Dec 23, 2015

@alexcrichton rewrote the test to use pipes instead of files and to re-exec itself instead of calling a shell

@alexcrichton
Copy link
Member

@bors: r+ 751fce0974dd4f634539c37ba759d13c4f894d09

Thanks!

@Manishearth
Copy link
Member

http://buildbot.rust-lang.org/builders/auto-win-gnu-64-nopt-t/builds/2525/steps/test/logs/stdio



---- [run-pass] run-pass/issue-30490.rs stdout ----

error: compilation failed!
status: exit code: 101
command: PATH="x86_64-pc-windows-gnu/stage2/bin;C:\bot\slave\auto-win-gnu-64-nopt-t\build\obj\x86_64-pc-windows-gnu\stage2\bin;C:\bot\slave\auto-win-gnu-64-nopt-t\build\obj\x86_64-pc-windows-gnu\llvm\Release\lib;C:\mingw-w64\x86_64-4.9.1-win32-seh-rt_v3-rev1\mingw64\bin;C:\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\msys64\usr\bin;C:\python27;C:\python27\scripts;C:\program files (x86)\inno setup 5;C:\program files (x86)\CMake\bin;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit;C:\Program Files\Microsoft SQL Server\110\Tools\Binn;C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0;C:\msys64\mingw64\bin" x86_64-pc-windows-gnu/stage2/bin/rustc.exe C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs -L x86_64-pc-windows-gnu/test/run-pass/ --target=x86_64-pc-windows-gnu -L x86_64-pc-windows-gnu/test/run-pass\issue-30490.stage2-x86_64-pc-windows-gnu.run-pass.libaux -C prefer-dynamic -o x86_64-pc-windows-gnu/test/run-pass\issue-30490.stage2-x86_64-pc-windows-gnu.exe --cfg rtopt -L x86_64-pc-windows-gnu/rt
stdout:
------------------------------------------

------------------------------------------
stderr:
------------------------------------------
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:26: 102:36 error: failed to resolve. Use of undeclared type or module `libc` [E0433]
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102      assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
                                                                                                        ^~~~~~~~~~
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:6: 102:61 note: in this expansion of assert_eq! (defined in <std macros>)
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:26: 102:36 help: run `rustc --explain E0433` to see a detailed explanation
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:26: 102:36 error: unresolved name `libc::pipe` [E0425]
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102      assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
                                                                                                        ^~~~~~~~~~
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:6: 102:61 note: in this expansion of assert_eq! (defined in <std macros>)
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:26: 102:36 help: run `rustc --explain E0425` to see a detailed explanation
<std macros>:5:8: 5:18 error: the type of this value must be known in this context
<std macros>:5 if ! ( * left_val == * right_val ) {
                      ^~~~~~~~~~
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:6: 102:61 note: in this expansion of assert_eq! (defined in <std macros>)
<std macros>:5:22: 5:33 error: the type of this value must be known in this context
<std macros>:5 if ! ( * left_val == * right_val ) {
                                    ^~~~~~~~~~~
C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/test/run-pass/issue-30490.rs:102:6: 102:61 note: in this expansion of assert_eq! (defined in <std macros>)
error: aborting due to 4 previous errors

------------------------------------------

thread '[run-pass] run-pass/issue-30490.rs' panicked at 'explicit panic', C:/bot/slave/auto-win-gnu-64-nopt-t/build/src/compiletest\runtest.rs:1505

bors added a commit that referenced this pull request Dec 25, 2015
* If the requested descriptors to inherit are stdio descriptors there
  are situations where they will not be set correctly
* Example: parent's stdout --> child's stderr
           parent's stderr --> child's stdout
* Solution: if the requested descriptors for the child are stdio
  descriptors, `dup` them before overwriting the child's stdio
@ipetkov
Copy link
Contributor Author

ipetkov commented Dec 25, 2015

Doh! Forgot some #[cfg(unix)] guards in the test case...

@alexcrichton @Manishearth should be fixed now

@alexcrichton
Copy link
Member

@bors: r+ 7f7a059

@bors
Copy link
Contributor

bors commented Jan 11, 2016

⌛ Testing commit 7f7a059 with merge 6f86431...

@bors
Copy link
Contributor

bors commented Jan 11, 2016

💔 Test failed - auto-win-msvc-32-opt

@alexcrichton
Copy link
Member

@bors: retry

On Mon, Jan 11, 2016 at 3:21 PM, bors notifications@github.com wrote:

[image: 💔] Test failed - auto-win-msvc-32-opt
http://buildbot.rust-lang.org/builders/auto-win-msvc-32-opt/builds/1871


Reply to this email directly or view it on GitHub
#30490 (comment).

@bors
Copy link
Contributor

bors commented Jan 11, 2016

⌛ Testing commit 7f7a059 with merge d228cd3...

bors added a commit that referenced this pull request Jan 11, 2016
* If the requested descriptors to inherit are stdio descriptors there
  are situations where they will not be set correctly
* Example: parent's stdout --> child's stderr
           parent's stderr --> child's stdout
* Solution: if the requested descriptors for the child are stdio
  descriptors, `dup` them before overwriting the child's stdio

Example of a program which exhibits the bug:
```rust
// stdio.rs
use std::io::Write;
use std::io::{stdout, stderr};
use std::process::{Command, Stdio};
use std::os::unix::io::FromRawFd;

fn main() {
    stdout().write_all("parent stdout\n".as_bytes()).unwrap();
    stderr().write_all("parent stderr\n".as_bytes()).unwrap();

    Command::new("sh")
        .arg("-c")
        .arg("echo 'child stdout'; echo 'child stderr' 1>&2")
        .stdin(Stdio::inherit())
        .stdout(unsafe { FromRawFd::from_raw_fd(2) })
        .stderr(unsafe { FromRawFd::from_raw_fd(1) })
        .status()
        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
}
```

Before:
```
$ rustc --version
rustc 1.7.0-nightly (8ad12c3 2015-12-19)
$ rustc stdio.rs && ./stdio >out 2>err
$ cat out
parent stdout
$ cat err
parent stderr
child stdout
child stderr
```

After (expected):
```
$ rustc --version
rustc 1.7.0-dev (712ecce 2015-12-19)
$ rustc stdio.rs && ./stdio >out 2>err
$ cat out
parent stdout
child stderr
$ cat err
parent stderr
child stdout
```
@bors bors merged commit 7f7a059 into rust-lang:master Jan 11, 2016
@ipetkov ipetkov deleted the unix-spawn branch January 12, 2016 03:49
@ijackson
Copy link
Contributor

ijackson commented Nov 9, 2021

I think the original report here shows an program which has undefined behaviour. The documentation for std::os::unix::io::FromRawFd says that it

consumes ownership of the specified file descriptor

Calling this on 0, 1 or 2 is clearly wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants