-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
libstd: Add thread unsafety warnings around setenv() and unsetenv() #24741
libstd: Add thread unsafety warnings around setenv() and unsetenv() #24741
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see CONTRIBUTING.md for more information. |
I read too quickly - Rust has its own mutex, but that doesn't help if there are other non-rust threads in the process. I'll update this PR. |
7668a1e
to
712cef9
Compare
Reworked to note that |
Thanks for the PR! I think this isn't quite the message we want to be sending, however, as "possibly unsafe" code is still unsafe and needs to be marked as such with It's possible that one day we will expose the environment variable lock to C to allow it to acquire it, but for now it's unlikely we'll commit to that so soon. |
Hi @alexcrichton , thanks for the review! I'm excited to make a small contribution to Rust =) On to the topic: It doesn't actually work to just wrap a mutex around See https://sourceware.org/bugzilla/show_bug.cgi?id=15607 (which is linked from https://bugs.freedesktop.org/show_bug.cgi?id=65681 ) So every C library user including glibc itself (for things like It's just not going to happen. Or even if it did happen today, we'd still have many years of people running old glibc/rust/other C libraries (such as glib, which is a project I contribute to and where we saw this bug in the real world). I'm happy to reword, but I don't think we can recommend external synchronization for the above reasons. |
Another option is to drop |
BTW, it looks like Also [1] I actually don't know offhand all of the details of Windows threadsafety around this, but https://public.kitware.com/Bug/view.php?id=13156#c29334 is a useful comment in a thread. |
True, but this is why the two functions in Rust returned owned buffers, and I think it's still reasonable to require that external users at least synchronize access, even when using external libraries, to have this kind of protection.
Unfortunately I think this may be a non-starter. Not only is it a large breaking change, but it's not cross-platform as Windows environment variables work fairly differently. |
Yes, there's no issues if one's program is 100% Rust. This PR is about
I would assert that very few Rust program authors are going to be in a
It'd be painful, yes. Ideally this would have been caught far earlier. It's possible that there are no Rust programs that are broken today - But several GNOME components were burned by this, as well as the Fedora On the plus side, the Rust |
Right, but it's not required to patch up existing libraries. This is perhaps just part of the contract of calling an Put another way this is not an inherent unsafety of this method, but rather a contract which needs to be upheld when crossing the FFI boundary on some platforms. |
Let me state this plainly: I assert that if one's application has both The only reliable thing to do is to call Here's a message from one of the glibc developers implying the above: Here's a link I just found to a POSIX spec bug on this: "I.e., any program that wants to use getenv simply must refrain from
What you're saying is that if I wanted my Rust app to use say Yes...except what if I'm trying to call a C library function which in In this case where the actual Let's take a real world example; say I'm writing Rust bindings for There deep in the bowels of a disk probing function, it happens to call (Ok, in the parted case above, at the most you'd have a read of freed Looks like a better example where the library is actually trying to And if you're still thinking that it's reasonable for the author of Rust There really is no sane solution to this other than banning setenv after |
The run and install commands were not seeing the environment, this will allow users to pass environment variables into atomic commands.
@cgwalters I definitely agree that this is a bug that comes up in practice, and it's definitely something that needs to be warned about, I think the message may just need to be updated slightly. For example, how about this?
|
712cef9
to
79bda49
Compare
I feel like that doesn't quite leave the reader with enough information to figure out "how do I do this synchronization" or in general find out more information about the issue. There doesn't seem to be significant precedent for linking to URLs from documentation, but how about something like ⬆️ ? Happy to do other URLs too (could we use this PR?) |
(That said, I'm fine to just do exactly what you suggest in the interest of moving on from this issue) |
Linking to an external site is definitely fine by me!
|
Assigned to @alexcrichton |
79bda49
to
92598cd
Compare
Ok, pushed an update which does both of the above. |
Thanks @cgwalters! It looks like there's only one piece remaining, the
|
See: https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9 https://bugs.freedesktop.org/show_bug.cgi?id=65681 I just noticed this while talking to someone who was using `os.environ['FOO'] = 'BAR'` in Python and since I'm learning Rust, I was curious if it did anything special here. It looks like Rust has an internal mutex, which helps for apps that are pure Rust, but it will be an evil trap for someone later adding in native code (apps like Servo and games will be at risk). Java got this right by disallowing `setenv()` from the start. I suggest Rust program authors only use `setenv()` early in main.
92598cd
to
44a5bf1
Compare
Oops, thanks. OK, now I know about |
…eadsafe, r=alexcrichton See: https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9 https://bugs.freedesktop.org/show_bug.cgi?id=65681 I just noticed this while talking to someone who was using `os.environ['FOO'] = 'BAR'` in Python and since I'm learning Rust, I was curious if it did anything special here (and the answer appears to be no). Java got this right by disallowing `setenv()` from the start.
Even without low-level races, This problem would not go away if we provided thread-safe environment access at the libc layer. Therefore, I'm surprised the function isn't marked |
@fweimer Can you use that behavior to demonstrate memory-unsafe behavior in a Rust program that does not use the |
Just for fun: from my previous comment
I just happened to notice my theoretical example exists now: https://github.com/pop-os/libparted |
Due to a race condition in Docker[1], we need to disable extraction using unpigz. Currently this is done in the image extraction code, however this code is multi-threaded. Setenv is not thread safe in C[2], so even thought it is safe in go[3], there's a small risk if there's any C threads running. It's safer to just set this env variable once when `oc` starts, rather every time the layerByEntry function is called. [1] moby/moby#39859 [2] rust-lang/rust#24741 [3] https://github.com/golang/go/blob/a38a917aee626a9b9d5ce2b93964f586bf759ea0/src/syscall/env_unix.go#L18
Invoking `setenv()` in multi-threaded processes is unsafe: rust-lang/rust#24741 https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9 https://bugs.freedesktop.org/show_bug.cgi?id=65681
Invoking `setenv()` in multi-threaded processes is unsafe: rust-lang/rust#24741 https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9 https://bugs.freedesktop.org/show_bug.cgi?id=65681
Invoking `setenv()` in multi-threaded processes is unsafe: rust-lang/rust#24741 https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9 https://bugs.freedesktop.org/show_bug.cgi?id=65681
See:
https://sourceware.org/bugzilla/show_bug.cgi?id=4887#c9
https://bugs.freedesktop.org/show_bug.cgi?id=65681
I just noticed this while talking to someone who was using
os.environ['FOO'] = 'BAR'
in Python and since I'm learning Rust, Iwas curious if it did anything special here (and the answer appears to
be no).
Java got this right by disallowing
setenv()
from the start.