]> git.proxmox.com Git - cargo.git/commitdiff
Fix deadlock when build scripts are waiting for input on stdin
authorArlo Siemsen <arsiem@microsoft.com>
Mon, 10 Oct 2022 15:43:39 +0000 (10:43 -0500)
committerArlo Siemsen <arsiem@microsoft.com>
Wed, 19 Oct 2022 05:28:55 +0000 (00:28 -0500)
crates/cargo-util/Cargo.toml
crates/cargo-util/src/process_builder.rs
tests/testsuite/build_script.rs

index 61a28dd5f7655be4ba8c1668f8aff9368d8f94f9..2ab4a60c26ec502cc7460e8b9768643e0befd282 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "cargo-util"
-version = "0.2.1"
+version = "0.2.2"
 edition = "2021"
 license = "MIT OR Apache-2.0"
 homepage = "https://github.com/rust-lang/cargo"
index 714cc595eff48deb7a8fe66630a0b2ec594d73d0..424c9089d1d7b1487f91a808d704bf259757802d 100644 (file)
@@ -40,7 +40,7 @@ pub struct ProcessBuilder {
     /// See [`ProcessBuilder::retry_with_argfile`] for more information.
     retry_with_argfile: bool,
     /// Data to write to stdin.
-    stdin: Vec<u8>,
+    stdin: Option<Vec<u8>>,
 }
 
 impl fmt::Display for ProcessBuilder {
@@ -82,7 +82,7 @@ impl ProcessBuilder {
             jobserver: None,
             display_env_vars: false,
             retry_with_argfile: false,
-            stdin: Vec::new(),
+            stdin: None,
         }
     }
 
@@ -212,7 +212,7 @@ impl ProcessBuilder {
 
     /// Sets a value that will be written to stdin of the process on launch.
     pub fn stdin<T: Into<Vec<u8>>>(&mut self, stdin: T) -> &mut Self {
-        self.stdin = stdin.into();
+        self.stdin = Some(stdin.into());
         self
     }
 
@@ -284,18 +284,22 @@ impl ProcessBuilder {
     fn _output(&self) -> io::Result<Output> {
         if !debug_force_argfile(self.retry_with_argfile) {
             let mut cmd = self.build_command();
-            match piped(&mut cmd).spawn() {
+            match piped(&mut cmd, self.stdin.is_some()).spawn() {
                 Err(ref e) if self.should_retry_with_argfile(e) => {}
                 Err(e) => return Err(e),
                 Ok(mut child) => {
-                    child.stdin.take().unwrap().write_all(&self.stdin)?;
+                    if let Some(stdin) = &self.stdin {
+                        child.stdin.take().unwrap().write_all(stdin)?;
+                    }
                     return child.wait_with_output();
                 }
             }
         }
         let (mut cmd, argfile) = self.build_command_with_argfile()?;
-        let mut child = piped(&mut cmd).spawn()?;
-        child.stdin.take().unwrap().write_all(&self.stdin)?;
+        let mut child = piped(&mut cmd, self.stdin.is_some()).spawn()?;
+        if let Some(stdin) = &self.stdin {
+            child.stdin.take().unwrap().write_all(stdin)?;
+        }
         let output = child.wait_with_output();
         close_tempfile_and_log_error(argfile);
         output
@@ -340,14 +344,14 @@ impl ProcessBuilder {
 
         let spawn = |mut cmd| {
             if !debug_force_argfile(self.retry_with_argfile) {
-                match piped(&mut cmd).spawn() {
+                match piped(&mut cmd, false).spawn() {
                     Err(ref e) if self.should_retry_with_argfile(e) => {}
                     Err(e) => return Err(e),
                     Ok(child) => return Ok((child, None)),
                 }
             }
             let (mut cmd, argfile) = self.build_command_with_argfile()?;
-            Ok((piped(&mut cmd).spawn()?, Some(argfile)))
+            Ok((piped(&mut cmd, false).spawn()?, Some(argfile)))
         };
 
         let status = (|| {
@@ -541,11 +545,15 @@ fn debug_force_argfile(retry_enabled: bool) -> bool {
     cfg!(debug_assertions) && env::var("__CARGO_TEST_FORCE_ARGFILE").is_ok() && retry_enabled
 }
 
-/// Creates new pipes for stderr, stdout and stdin.
-fn piped(cmd: &mut Command) -> &mut Command {
+/// Creates new pipes for stderr, stdout, and optionally stdin.
+fn piped(cmd: &mut Command, pipe_stdin: bool) -> &mut Command {
     cmd.stdout(Stdio::piped())
         .stderr(Stdio::piped())
-        .stdin(Stdio::piped())
+        .stdin(if pipe_stdin {
+            Stdio::piped()
+        } else {
+            Stdio::null()
+        })
 }
 
 fn close_tempfile_and_log_error(file: NamedTempFile) {
index 4a3ba930a127fc9dcc46a8472a63a8521a0d7bbc..b5ac3b04acc467f49204f7f5d29b15b31773cf06 100644 (file)
@@ -4905,3 +4905,29 @@ for more information about build script outputs.
         )
         .run();
 }
+
+#[cargo_test]
+fn custom_build_closes_stdin() {
+    // Ensure stdin is closed to prevent deadlock.
+    // See https://github.com/rust-lang/cargo/issues/11196
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+                [package]
+                name = "foo"
+                version = "0.5.0"
+                build = "build.rs"
+            "#,
+        )
+        .file("src/main.rs", "fn main() {}")
+        .file(
+            "build.rs",
+            r#"fn main() {
+                let mut line = String::new();
+                std::io::stdin().read_line(&mut line).unwrap();
+            }"#,
+        )
+        .build();
+    p.cargo("build").run();
+}