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

Regression in Command::exec's PATH resolution #55775

Closed
euank opened this issue Nov 8, 2018 · 14 comments · Fixed by #55939
Closed

Regression in Command::exec's PATH resolution #55775

euank opened this issue Nov 8, 2018 · 14 comments · Fixed by #55939
Labels
regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Milestone

Comments

@euank
Copy link

euank commented Nov 8, 2018

Using env_clear() + exec() currently exhibits different behaviour on nightly and stable.

I believe the first nightly to regress was 2018-11-02.

A minimal reproduction is the following code:

use std::process::Command;
use std::os::unix::process::CommandExt;

fn main() {
    let mut cmd = Command::new("true");
    cmd.env_clear();
    let err = cmd.exec();
    println!("error: {}", err);
}

On stable, it prints no error. On nightly, it prints error: No such file or directory (os error 2).

Using strace -e trace=execve, we can see the difference:

# stable
execve("/bin/true", ["true"], 0x7fedf1821010 /* 0 vars */) = 0
# nightly
execve("true", ["true"], 0x561a58edbb80 /* 0 vars */) = -1 ENOENT (No such file or directory)

Meta

$ rustc --version --verbose
rustc 1.32.0-nightly (15d770400 2018-11-06)
binary: rustc
commit-hash: 15d770400eed9018f18bddf83dd65cb7789280a5
commit-date: 2018-11-06
host: x86_64-unknown-linux-gnu
release: 1.32.0-nightly
LLVM version: 8.0
euank added a commit to euank/pazi that referenced this issue Nov 8, 2018
This avoids a regression in nightly (reported
rust-lang/rust#55775).

It makes sense to pass through the path after 'env_clear' anyway though
given shells are just run by bin name, not full bin path.
@phansch
Copy link
Member

phansch commented Nov 8, 2018

Probably due to #55359

@Mark-Simulacrum
Copy link
Member

Cc @rust-lang/libs @alexcrichton

@alexcrichton
Copy link
Member

Thanks for the report! I'm... not actually sure how this ever passed on stable, it wasn't really ever intended to. #55359 is almost for sure the cause of this, though

cc @alex too

@alexcrichton alexcrichton added I-nominated regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Nov 8, 2018
@alex
Copy link
Member

alex commented Nov 8, 2018

I'm not sure I understand what the desired behavior is. We look up $PATH in the child process's environment, that's the decided on behavior. If the child is modifying unrelated environment variables, $PATH is the same as the parent's, and if $PATH itself is modified we use that. Is the desired behavior here that if env is cleared then we should look at the parent's $PATH instead? And does this apply to env_remove as well?

I'm concerned that this behavior is awfully complicated, and not consistent with any platform APIs. That said, I'm happy to implement this if we can clearly articulate what the desired behavior is.

@Mark-Simulacrum
Copy link
Member

I believe we've backported the relevant PR to beta as well so marking as regression from stable to beta

@Mark-Simulacrum Mark-Simulacrum added regression-from-stable-to-beta Performance or correctness regression from stable to beta. and removed regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. labels Nov 8, 2018
@Mark-Simulacrum Mark-Simulacrum added this to the 1.31 milestone Nov 8, 2018
@alex
Copy link
Member

alex commented Nov 8, 2018

Beta backport hasn't landed yet, #55611.

@alexcrichton
Copy link
Member

@alex there's not really a "desired behavior" in this case, at this point it's just whack-a-mole of trying to rationalize with what we have so far.

The original implementation happened to have the side effect of using the child's PATH to do the binary lookup. This was then duplicated on Windows. That was probably a mistake. The ship has sailed, however, and now we need to make sure bugs are fixed and hopefully not break too much.

@alex
Copy link
Member

alex commented Nov 8, 2018

Ok. I'll try to get a patch for this by this evening.

@euank
Copy link
Author

euank commented Nov 8, 2018

And does this apply to env_remove as well?

Yes, env_remove also technically exhibits the same regression, though it seems somewhat less likely to me that someone might accidentally remove PATH via env_remove("PATH") than via env_clear().

Perhaps it could be argued that the env_remove case is unlikely to impact anyone... though, with a github search, I found one use ofenv_remove that looks like it would break.

I know that case uses spawn, not exec, but after checking, it turns out spawn exhibits the same change in behaviour, presumably because it uses do_exec under the hood.

I agree the new behaviour makes more sense, but I think backwards compatibility within 1.0 is more important if it's reasonably achievable.

Thank you for the quick response by the way!

@alex
Copy link
Member

alex commented Nov 8, 2018

PR is up at #55793

@alexcrichton
Copy link
Member

@euank can you elaborate a bit more on your use case for this? After browsing glibc's source in #55793 it looks like this would only have previously worked for binaries in /bin or /usr/bin (because that's hardcoded in glibc). Is that the behavior you're seeing as well?

@euank
Copy link
Author

euank commented Nov 9, 2018

can you elaborate a bit more on your use case for this?

I wouldn't say that what I have is a use-case per-se, it's simply code that happened to work with env_clear and then eventually broke on nightly. I don't mind setting my own PATH in my code if needed -- env_clear was meant as a shorthand to remove a bunch of other variables, not to do anything PATH related.

My reason for reporting this issue is because it's a regression which might impact other users.
If we still want to make a decision to keep the new behavior, at least it will be a conscious decision to to potentially break some users.

looks like this would only have previously worked for binaries in /bin or /usr/bin

I didn't realize that, but indeed the commands I'm running are in /usr/bin.

@alexcrichton
Copy link
Member

Ok thanks for the info! Just wanted to confirm at least, and the report is still very much appreciated!

alex pushed a commit to alex/rust that referenced this issue Nov 9, 2018
… of PATH.

This restores the previous behavior where if env_clear() or env_remove("PATH") was used we fall back to a default PATH of "/bin:/usr/bin"
pietroalbini added a commit to pietroalbini/rust that referenced this issue Nov 12, 2018
…r=alexcrichton

Fixes rust-lang#55775 -- fixed regression in Command::exec's handling of PATH.

This restores the previous behavior where if env_clear() or env_remove() was used, the parent's PATH would be consulted.

r? @alexcrichton
alex pushed a commit to alex/rust that referenced this issue Nov 13, 2018
… of PATH.

This restores the previous behavior where if env_clear() or env_remove("PATH") was used we fall back to a default PATH of "/bin:/usr/bin"
alexcrichton added a commit to alexcrichton/rust that referenced this issue Nov 13, 2018
This commit, after reverting rust-lang#55359, applies a different fix for rust-lang#46775
while also fixing rust-lang#55775. The basic idea was to go back to pre-rust-lang#55359
libstd, and then fix rust-lang#46775 in a way that doesn't expose rust-lang#55775.

The issue described in rust-lang#46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes rust-lang#55775

Co-authored-by: Alex Gaynor <[email protected]>
bors added a commit that referenced this issue Nov 14, 2018
std: Synchronize access to global env during `exec`

This commit, after reverting #55359, applies a different fix for #46775
while also fixing #55775. The basic idea was to go back to pre-#55359
libstd, and then fix #46775 in a way that doesn't expose #55775.

The issue described in #46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes #55775
alexcrichton added a commit to alexcrichton/rust that referenced this issue Nov 14, 2018
This commit, after reverting rust-lang#55359, applies a different fix for rust-lang#46775
while also fixing rust-lang#55775. The basic idea was to go back to pre-rust-lang#55359
libstd, and then fix rust-lang#46775 in a way that doesn't expose rust-lang#55775.

The issue described in rust-lang#46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes rust-lang#55775

Co-authored-by: Alex Gaynor <[email protected]>
bors added a commit that referenced this issue Nov 14, 2018
std: Synchronize access to global env during `exec`

This commit, after reverting #55359, applies a different fix for #46775
while also fixing #55775. The basic idea was to go back to pre-#55359
libstd, and then fix #46775 in a way that doesn't expose #55775.

The issue described in #46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes #55775
alexcrichton added a commit to alexcrichton/rust that referenced this issue Nov 15, 2018
This commit, after reverting rust-lang#55359, applies a different fix for rust-lang#46775
while also fixing rust-lang#55775. The basic idea was to go back to pre-rust-lang#55359
libstd, and then fix rust-lang#46775 in a way that doesn't expose rust-lang#55775.

The issue described in rust-lang#46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes rust-lang#55775

Co-authored-by: Alex Gaynor <[email protected]>
@alexcrichton
Copy link
Member

The backport of the fix for this (#55939) is posted at #55978

ischeinkman pushed a commit to ischeinkman/libnx-rs-std that referenced this issue Dec 20, 2018
This commit, after reverting rust-lang#55359, applies a different fix for rust-lang#46775
while also fixing rust-lang#55775. The basic idea was to go back to pre-rust-lang#55359
libstd, and then fix rust-lang#46775 in a way that doesn't expose rust-lang#55775.

The issue described in rust-lang#46775 boils down to two problems:

* First, the global environment is reset during `exec` but, but if the
  `exec` call fails then the global environment was a dangling pointer
  into free'd memory as the block of memory was deallocated when
  `Command` is dropped. This is fixed in this commit by installing a
  `Drop` stack object which ensures that the `environ` pointer is
  preserved on a failing `exec`.

* Second, the global environment was accessed in an unsynchronized
  fashion during `exec`. This was fixed by ensuring that the
  Rust-specific environment lock is acquired for these system-level
  operations.

Thanks to Alex Gaynor for pioneering the solution here!

Closes rust-lang#55775

Co-authored-by: Alex Gaynor <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
5 participants