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

Child process can be spawned even when tokio::process::Command::spawn returns an error #6797

Open
Maki325 opened this issue Aug 25, 2024 · 4 comments · May be fixed by #6803
Open

Child process can be spawned even when tokio::process::Command::spawn returns an error #6797

Maki325 opened this issue Aug 25, 2024 · 4 comments · May be fixed by #6803
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-process Module: tokio/process

Comments

@Maki325
Copy link

Maki325 commented Aug 25, 2024

Version
Tokio v1.39.2
rustc 1.76.0

Platform
WSL2 Ubuntu 20.04.6 LTS

Description
For both unix and windows imp for tokio::process::Command::spawn, spawning the std::process::Child is the first call. That could leave the child alive even if subsequent function calls fail.

In my case a panic happened, because I used the Tokio Child outside the Tokio runtime, which isn't the same as the spawn returning an error. But it's still seems possible for an error to happen in the function, which would leave the Child process alive, without the user being able to kill it, or access it in any way.

I could be wrong about it though, as I don't know if those functions in that order would never fail. Maybe they could only fail if the child got killed, and the panic is the only way for this to happen. Which would be fine, as the child would then not end up unreachable, but still running. I have tried looking deeper into the source code to check if that's the case, but it gets much deeper than my understanding of tokio/async runtimes.

Minimum code recreation
Calling tokio::process::Command::spawn outside the tokio runtime.

fn main() {
    let response = tokio::process::Command::new("ls").spawn();
    println!("{response:#?}");
}

This will cause a panic, but the ls command will still output the results to the terminal.

How to maybe handle this
I thought of two ways to handle this. Either:

  • Return the std::process::Child as part of the error, so the user can handle it how they want
  • Send SIGKILL to the child, to make sure that it's dead when erroring
@Maki325 Maki325 added A-tokio Area: The main tokio crate C-bug Category: This is a bug. labels Aug 25, 2024
@Darksonn Darksonn added the M-process Module: tokio/process label Aug 25, 2024
@Darksonn
Copy link
Contributor

@ipetkov Thoughts?

@ipetkov
Copy link
Member

ipetkov commented Aug 25, 2024

We mirror the behavior of the standard library here: (example of the same behavior with using only std code)

Similar to the behavior to the standard library, and unlike the futures paradigm of dropping-implies-cancellation, a spawned process will, by default, continue to execute even after the Child handle has been dropped.

If you want to ensure that a child process is killed whenever its handle is dropped (even during panic unwinding), you can use kill_on_drop(true)

@ipetkov
Copy link
Member

ipetkov commented Aug 25, 2024

Oh looks like kill_on_drop might not work with this exact panic since we haven't wrapped the inner child in our kill-on-drop wrapper. We should probably both do any checks which can panic sooner, and wrap the intermediate value as appropriate sooner

@Maki325
Copy link
Author

Maki325 commented Aug 25, 2024

The child should probably be wrapped with kill-on-drop as soon as it's spawned, and if everything is successful just unwrap it, so that the tokio::process::Command::spawn can wrap it itself.

If it's ok, I can try implementing it, and making a pull request.

Maki325 added a commit to Maki325/tokio that referenced this issue Aug 27, 2024
There's a chance that between spawning of the `std Child`, and wrapping
it into a tokio impl an error can occurs, which would leave the child
alive, with no way to access it from the user side and not being part
of the tokio runtime.

This commit fixes that issue by wrapping the `std Child` right after
spawning with a struct that implements Drop, where the child would
be killed if it's dropped.

Fixes: tokio-rs#6797
@Maki325 Maki325 linked a pull request Aug 27, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-process Module: tokio/process
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants