-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
std::os::unix::process::CommandExt::exec should be unsafe #46775
Comments
Smells like #39575 , marking as unsound. |
@Diggsey If you could write some PoC code which demonstrates this, it'd be very helpful. This came up on chat today. |
@joshlf not right now, but I can point you to the code:
If you look at the comments on that method, you can see that it's clearly been written under the assumption that it will be running in a child process (immediately after a fork). If this were the case, the code would be correct, because it would be guaranteed that there are no other threads running in the child, due to the nature of |
I think there's an even more simple memory unsafety here, unless I'm missing something:
In other words, any attempt to use the environment after a failed exec is memory unsafe, because the global environment is now pointing at some random heap memory? Have I grievously missed something? |
POC:
|
It seems like a reasonable fix would be to use |
@alex This does not reproduce on playground, likely because asan doesn't run there. Could you describe what is going on, for someone not knowing how env vars are implemented on Linux/Unix platforms? |
On Linux, env vars are really just a pointer to an array of pointers to nul-terminated strings, with a static lifetime. So in this case we set that pointer to a Vec's, which is on the stack, data buffer. And then that pointer lives beyond the lifetime of the value on the stack. Now all reads/writes to env vars are pointing to freed memory. A classic UAF. FWIW a reproducer that will probably work regardless of memory allocator is to check if an env var exists before the You'll notice that I didn't get an ASAN report, that's because the standard lib code doing the read wasn't compiled with ASAN, but I do get the SEGV because it uses an allocator where as soon as some memory is freed it becomes unmapped. |
Thanks!
Changing the env var ptr is happening here, right? rust/src/libstd/sys/unix/process/process_unix.rs Lines 196 to 198 in 365b900
Also I was confused a bit here because I thought you said the data buffer is on the stack. But what you mean is that the Seems like an "easy fix" would be to |
Using |
Reading the man page I agree, it seems designed for this situation (changing the executed program's environment). Can we assume that it would also solve the data race pointed out at the beginning of this issue? |
Yes, it would.
…On Thu, Oct 25, 2018 at 8:29 AM Ralf Jung ***@***.***> wrote:
Reading the man page I agree, it seems designed for this situation
(changing the executed program's environment).
Can we assume that it would also solve the data race pointed out at the
beginning of this issue?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#46775 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAADBDwbERYUnTg6C6IxLlObFp1Jl8nGks5uoa7HgaJpZM4REa02>
.
--
All that is necessary for evil to succeed is for good people to do nothing.
|
I'll try to whip up a patch for this. |
Awesome :) |
…mmand::exec Instead, pass the environment to execvpe, so the kernel can apply it directly to the new process. This avoids a use-after-free in the case where exec'ing the new process fails for any reason, as well as a race condition if there are other threads alive during the exec.
Fixes #46775 -- don't mutate the process's environment in Command::exec Instead, pass the environment to execvpe, so the kernel can apply it directly to the new process. This avoids a use-after-free in the case where exec'ing the new process fails for any reason, as well as a race condition if there are other threads alive during the exec. Fixes #46775
…mmand::exec Instead, pass the environment to execvpe, so the kernel can apply it directly to the new process. This avoids a use-after-free in the case where exec'ing the new process fails for any reason, as well as a race condition if there are other threads alive during the exec.
…nt in Command::exec" This reverts commit 36fe3b6.
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 <alex.gaynor@gmail.com>
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
…nt in Command::exec" This reverts commit 36fe3b6.
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 <alex.gaynor@gmail.com>
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
…nt in Command::exec" This reverts commit 36fe3b6.
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 <alex.gaynor@gmail.com>
…mmand::exec Instead, pass the environment to execvpe, so the kernel can apply it directly to the new process. This avoids a use-after-free in the case where exec'ing the new process fails for any reason, as well as a race condition if there are other threads alive during the exec.
…nt in Command::exec" This reverts commit 36fe3b6.
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 <alex.gaynor@gmail.com>
This method assigns to
environ
outside of any lock, meaning that it's automatically unsafe for multi-threaded programs.For single-threaded programs, if the
exec
fails,environ
will point to memory that has been freed. The documentation gives a false sense of security:The text was updated successfully, but these errors were encountered: