-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were leaked to child processes on Windows. On Windows once a handle is in overlapped mode it should always have I/O executed with an instance of `OVERLAPPED`. Most child processes, however, are not prepared to have their stdio handles in overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle. Now we haven't had any odd behavior in Rust up to this point, and the original bug was introduced almost a year ago. I believe this is because it turns out that if you *don't* pass an `OVERLAPPED` then the system will [supply one for you][link]. In this case everything will go awry if you concurrently operate on the handle. In Rust, however, the stdio handles are always locked, and there's no way to not use them unlocked in libstd. Due to that change we've always had synchronized access to these handles, which means that Rust programs typically "just work". Conversely, though, this commit fixes the test case included, which exhibits behavior that other programs Rust spawns may attempt to execute. Namely, the stdio handles may be concurrently used and having them in overlapped mode wreaks havoc. [link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343 Closes #38811
- Loading branch information
1 parent
05f4a75
commit 5148918
Showing
4 changed files
with
153 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use std::env; | ||
use std::io::prelude::*; | ||
use std::process::Command; | ||
use std::thread; | ||
|
||
const THREADS: usize = 20; | ||
const WRITES: usize = 100; | ||
const WRITE_SIZE: usize = 1024 * 32; | ||
|
||
fn main() { | ||
let args = env::args().collect::<Vec<_>>(); | ||
if args.len() == 1 { | ||
parent(); | ||
} else { | ||
child(); | ||
} | ||
} | ||
|
||
fn parent() { | ||
let me = env::current_exe().unwrap(); | ||
let mut cmd = Command::new(me); | ||
cmd.arg("run-the-test"); | ||
let output = cmd.output().unwrap(); | ||
assert!(output.status.success()); | ||
assert_eq!(output.stderr.len(), 0); | ||
assert_eq!(output.stdout.len(), WRITES * THREADS * WRITE_SIZE); | ||
for byte in output.stdout.iter() { | ||
assert_eq!(*byte, b'a'); | ||
} | ||
} | ||
|
||
fn child() { | ||
let threads = (0..THREADS).map(|_| { | ||
thread::spawn(|| { | ||
let buf = [b'a'; WRITE_SIZE]; | ||
for _ in 0..WRITES { | ||
write_all(&buf); | ||
} | ||
}) | ||
}).collect::<Vec<_>>(); | ||
|
||
for thread in threads { | ||
thread.join().unwrap(); | ||
} | ||
} | ||
|
||
#[cfg(unix)] | ||
fn write_all(buf: &[u8]) { | ||
use std::fs::File; | ||
use std::mem; | ||
use std::os::unix::prelude::*; | ||
|
||
let mut file = unsafe { File::from_raw_fd(1) }; | ||
let res = file.write_all(buf); | ||
mem::forget(file); | ||
res.unwrap(); | ||
} | ||
|
||
#[cfg(windows)] | ||
fn write_all(buf: &[u8]) { | ||
use std::fs::File; | ||
use std::mem; | ||
use std::os::windows::raw::*; | ||
use std::os::windows::prelude::*; | ||
|
||
const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32; | ||
|
||
extern "system" { | ||
fn GetStdHandle(handle: u32) -> HANDLE; | ||
} | ||
|
||
let mut file = unsafe { | ||
let handle = GetStdHandle(STD_OUTPUT_HANDLE); | ||
assert!(!handle.is_null()); | ||
File::from_raw_handle(handle) | ||
}; | ||
let res = file.write_all(buf); | ||
mem::forget(file); | ||
res.unwrap(); | ||
} |