-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
set_var/remove_var are unsound in combination with C code that reads the environment #27970
Comments
cc @luqmana |
If we did take this route I'd want to convert the environment lock to a rwlock to ensure that we could at least have parallel dns queries. Also cc #27705. |
Yeah, either way the situation is tricky. We get tangled up in glibc internals. |
I found the wording on this page also particularly interesting:
The getaddrinfo function is indeed marked with this tag |
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 |
Triage: as far as I know, this hasn't changed at all. The docs do describe this possible footgun. |
This issue can also apply to any external crate that calls into something that calls get_env, for example time-rs/time#293 and chronotope/chrono#499. Maybe some way for these crates to observe the lock is called for? |
Seems to me like this is a soundness issue, and should be marked I-unsound? |
Assigning |
Just repeating what I've said on Zulip: To resolve the soundness bug in time & chrono, one option would be to provide a |
The env lock has to be exposed somehow, I agree. An alternative API would be a function that takes a closure that will be called with the lock held. This is less powerful (which might be a good thing: provide the least powerful API that is good enough). |
However, talking of the env lock -- also see #64718 for how the current use of the env lock is wrong, so exposing it in that state might be a problem. |
Sure, that would work as well. End result is the same, if done properly. |
Honestly, I don’t believe In a pure-Rust program, it is already safe, but in C, Even without the thread safety problems, there are other reasons that What might be safe is a function that fails if multiple threads are running, and which only allows setting a limited number of env vars, such as the timezone. |
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308.
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308.
Make `std::env::{set_var, remove_var}` unsafe in edition 2024 Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308.
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
Make `std::env::{set_var, remove_var}` unsafe in edition 2024 Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
Make `std::env::{set_var, remove_var}` unsafe in edition 2024 Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
I've posted a patch that should fix this for glibc: Reviews would be appreciated. It does not solve the conceptual problem that the environment is a process-global resource. Other threads still observe temporary environment changes intended for the current thread only. Just the crashes and missed variable lookups are gone. |
Just to clarify, your patch doesn't fix it "enough" for Rust to consider them to |
I don't see how this provides any significant thread-safety. The core unsafety of modifying the environment is that the lifetime of a string returned by |
Another way is for |
From the patch:
So, seems to me like setenv is already leaking, and this patch builds on that. Very nice to see progress on this! |
Even if that patch is accepted in glibc, POSIX doesn't guarantee thread safety, so this cannot change the decision for Rust (unless you get a change into musl, all the *BSDs, OpenSolaris, Darwin/Mac OS X etc, as well as the POSIX standard itself). And I don't foresee that happening due to backward compatibility around Still, it is a step in the right direction. |
Well, if it weren't for direct access to environ and the putenv issue, there could possibly be a safe function that is only available on platforms that use a new enough version of glibc. |
Note that Rust doesn't strictly adhere to POSIX, Rust is fine with stuff that all the platforms implement. E.g. I haven't looked into whether musl, *BSDs or Darwin implement synchronization for environment variables. I just wanted to say that POSIX itself might not be a requirement. |
What if another thread reads environ directly? Would that still be undefined behavior unless setenv uses an atomic operation to swap the pointer, and the reader uses an atomic load to read environ? |
@tmccombs Is this a question about the proposed new glibc implementation? It's not possible to ensure a proper snapshot with just atomic access to |
My point is that even if a c library reads from environ directly, then no amount of synchronization around setenv in rust would be safe in a multi-threaded program. |
Fix for flaky task test, not using `remove_var` because of: https://doc.rust-lang.org/std/env/fn.remove_var.html > Even though this function is currently not marked as unsafe, it needs to be because invoking it can cause undefined behaviour. The function will be marked unsafe in a future version of Rust. This is tracked in [rust#27970](rust-lang/rust#27970). > This function is safe to call in a single-threaded program. > In multi-threaded programs, you must ensure that are no other threads concurrently writing or reading(!) from the environment through functions other than the ones in this module. You are responsible for figuring out how to achieve this, but we strongly suggest not using set_var or remove_var in multi-threaded programs at all.
Like the documentation for our
set_var
(setenv) says, care must be taken with mutating environment variables in a multithreaded program. See this glibc bug #13271 that says getaddrinfo may call getenv.It looks like we have an unsynchronized call to getaddrinfo and this may cause trouble with glibc/Linux.
Seeing glibc's attitude to setenv in multithreaded programs,
set_var
seems like a big hazard in general(?).Discovered as an issue tangential to #27966
cc @alexcrichton
The text was updated successfully, but these errors were encountered: