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

Future deprecation of env::{set, remove}_var #92365

Closed
wants to merge 1 commit into from

Conversation

jhpratt
Copy link
Member

@jhpratt jhpratt commented Dec 28, 2021

At some point in the future it is quite likely that std::env::set_var and std::env::remove_var will be deprecated. There appears to be consensus that this should happen per a poll on IRLO. The only blocker to actually deprecating these methods is agreement on what should replace them. This PR serves two purposes: indicate they will be deprecated in documentation and trigger the deprecated_in_future lint.

@rustbot label +S-waiting-on-review +T-libs-api

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Dec 28, 2021
@rust-highfive
Copy link
Collaborator

r? @kennytm

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-log-analyzer
Copy link
Collaborator

The job mingw-check failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
    Checking hashbrown v0.11.0
    Checking object v0.26.2
    Checking miniz_oxide v0.4.0
    Checking addr2line v0.16.0
error: use of function `env::remove_var` that will be deprecated in a future Rust version: method is unsound
   |
46 |                 env::remove_var(k);
   |                      ^^^^^^^^^^
   |
   |
   = note: `-D deprecated-in-future` implied by `-D warnings`

error: use of function `env::set_var` that will be deprecated in a future Rust version: method is unsound
   |
51 |                 env::set_var(key, val);
   |                      ^^^^^^^


error: use of function `env::remove_var` that will be deprecated in a future Rust version: method is unsound
   |
53 |                 env::remove_var(key);
   |                      ^^^^^^^^^^

@jhpratt
Copy link
Member Author

jhpratt commented Dec 28, 2021

Hmm…not sure how we can avoid triggering the lint. I'm fairly certain rustc itself needs environment variables.

@joshtriplett
Copy link
Member

@jhpratt I think you'd have to allow the lint in those cases.

Also, rather than "method is unsound", I'd suggest "method cannot be made thread-safe".

@jhpratt
Copy link
Member Author

jhpratt commented Dec 28, 2021

How about "method is unsound because it cannot be made thread-safe"? I think any message without "unsound" is not ideal.

@@ -327,6 +327,7 @@ impl Error for VarError {
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
/// ```
#[stable(feature = "env", since = "1.0.0")]
#[rustc_deprecated(since = "TBD", reason = "method is unsound")]
Copy link
Member

@bjorn3 bjorn3 Dec 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link to the discussion about why it is unsound?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which discussion is preferred? I'm pretty sure at this point we could assemble a book with the various discussions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Maybe open a tracking issue to be refenced here and write a recap there together with all discussion you can find?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to defer changing the documentation of this function to another PR, or should we do it here? If we're going to do it as part of this PR then I'm happy to suggest changes to the docs.

@joshtriplett
Copy link
Member

joshtriplett commented Dec 28, 2021

@jhpratt Perhaps "method is unsound in multithreaded programs"?

EDIT: I like @mathstuf's suggestion even better: "method is unsound in the presence of threads".

@mathstuf
Copy link
Contributor

How about "method is unsound in the presence of threads"? Multi-threaded programs may have periods where no other threads exist. It is also ok post-fork (where no threads exist anymore).

@KamilaBorowska
Copy link
Contributor

KamilaBorowska commented Dec 29, 2021

It is also ok post-fork (where no threads exist anymore).

EDIT: This is actually incorrect, see later comment.

While technically true, Rust provides Command::{env, env_clear, env_remove, envs} for setting up the environment before spawning a process. I'm aware that fork is technically not only for spawning processes, however I think it's acceptable to require libc::setenv calls in programs that use libc::fork anyway.

@RalfJung
Copy link
Member

trigger the deprecated_in_future lint.

I don't think we should trigger that lint without giving some guidance on what people should do instead. As-is, the likely thing people will do is allow that lint, because they do not really have any other good options. So for now I don't think it makes sense to trigger that lint.

@jhpratt
Copy link
Member Author

jhpratt commented Dec 29, 2021

@RalfJung I presume that would require altering the lint itself?

@RalfJung
Copy link
Member

Well, it would require not adding the attribute. It might make sense to have some discussion of the issue in the doc comment and explain that a better replacement will come Eventually (TM), but I don't think we should add the attribute at this point.

@mathstuf
Copy link
Contributor

While technically true, Rust provides Command::{env, env_clear, env_remove, envs} for setting up the environment before spawning a process.

I'm well aware of that, but there are other tasks which are not exposed via Command which may need to be done as well (ulimit modifications, dropping privs, pledge, capability setup, namespace setup, etc. all come to mind at least) which would require an explicit fork in practice. I think explicitly mentioning that the special "twilight zone" of post-fork, pre-exec is fine in the docs at least (it already has numerous restrictions anyways, so one place it is actually allowed to assume more would be useful).

@jhpratt
Copy link
Member Author

jhpratt commented Dec 30, 2021

So is this something that's currently wanted? I don't particularly mind either way; it's not like this was a difficult PR to put together. Simple 👍/👎 from people that are relevant I suppose?

@leo60228
Copy link
Contributor

Maybe add an unstable copy of today's set_env as set_env_unsafe with a note that it's almost certainly subject to change before stabilizing?

@KamilaBorowska
Copy link
Contributor

KamilaBorowska commented Dec 30, 2021

How about "method is unsound in the presence of threads"? Multi-threaded programs may have periods where no other threads exist. It is also ok post-fork (where no threads exist anymore).

Okay, I updated my previous comment to note it's incorrect. According to POSIX:

A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called.

setenv is not an async-signal-safe function, as such it's not guaranteed to work after fork in a multi-threaded program. As such, for setting up environment variables, you are better of using Command::{env, env_clear, env_remove, envs}. If you need to do additional set-up in addition to setting up environment variables, Rust provides pre_exec function specifically for that purpose, see https://doc.rust-lang.org/stable/std/os/unix/process/trait.CommandExt.html#tymethod.pre_exec.

@mathstuf
Copy link
Contributor

setenv is not an async-signal-safe function, as such it's not guaranteed to work after fork in a multi-threaded program

Oh, indeed. There may be a lock behind that call that is still locked after the fork() I suppose. Fun.

bors added a commit to rust-lang-ci/rust that referenced this pull request Jan 1, 2022
…Simulacrum

Remove CommandEnv::apply

It's not being used and uses unsound set_var and remove_var functions. This is an internal function that isn't exported (even with `process_internals` feature), so this shouldn't break anything.

Also see rust-lang#92365. Note that this isn't the only use of those methods in standard library, so that particular pull request will need more changes than just this to work (in particular, `test_capture_env_at_spawn` is using `set_var` and `remove_var`).
@briansmith
Copy link
Contributor

Hmm…not sure how we can avoid triggering the lint. I'm fairly certain rustc itself needs environment variables.

If rustc is setting or unsetting environment variables itself, either directly or indirectly via its dependencies, we should attempt to change that before or during the process of this deprecation. If the uses in rustc are too hard to remove then what hope do other users have?

@jhpratt
Copy link
Member Author

jhpratt commented Jan 4, 2022

Right now it's a future deprecation — it's not actually being deprecated in this PR. The lint is allow by default. Though I do agree that there needs to be a replacement before actually deprecating (and I believe there's consensus on that point).

@leo60228
Copy link
Contributor

leo60228 commented Jan 4, 2022

The lint is allow by default.

Yes, but rustc and libstd have #![warn(deprecated_in_future)].

@jhpratt
Copy link
Member Author

jhpratt commented Jan 4, 2022

That's why it's an issue for this PR but not others, generally speaking. Other users don't have worry about builds failing because of a future deprecation unless they've explicitly opted into the lint.

@RalfJung
Copy link
Member

RalfJung commented Jan 4, 2022

So is this something that's currently wanted? I don't particularly mind either way; it's not like this was a difficult PR to put together. Simple +1/-1 from people that are relevant I suppose?

To explain my 👎 : I think we should not add a deprecated attribute until the replacement is in place. I have no objection to documentation changes, though.

@JohnCSimon JohnCSimon added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 23, 2022
@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 27, 2022
@joshtriplett
Copy link
Member

joshtriplett commented Mar 2, 2022

This seems to be somewhat stalled.

I do think we should have at least some plan for what we intend to support as a replacement, though that replacement doesn't have to exist in stable Rust for us to add deprecated_in_future. (Such a requirement seems more appropriate for deprecated.)

At the moment, the only proposal I've seen that seems like a viable replacement is to add unsafe functions. Apart from bikeshedding the names of those methods (std::env::set vs std::env::set_var_unsafe for instance) I don't think there's another proposal on the table that would still provide a mechanism to access the environment.

Would someone be interested in submitting a PR adding unsafe get and set and unset functions? I think we could merge that PR (with the functions unstable), then merge this future-deprecation, then consider actual deprecation if and when we stabilize the replacement functions.

@jhpratt
Copy link
Member Author

jhpratt commented Mar 2, 2022

I can do that tonight. Should just amount to calling the same thing internally.

@RalfJung
Copy link
Member

RalfJung commented Mar 2, 2022

I think the intention is for get to remain safe and unchanged.

Only mutating the environment is unsafe.

@Mark-Simulacrum
Copy link
Member

I believe the goal is to only mark env::set_var as unsafe, and to do so in-place (i.e., with a new custom deprecated_safe or similar attribute), based on the latest discussions in libs-api that I recall. My understanding is that we want to leave env::var safe in Rust's standard library, as the retrieval of environment variables is safe in correctly written programs (i.e., those that don't have any setenv/putenv/etc calls in the possibly MT section).

I think the next step before actually deprecating these would be to stabilize #93346, which tracks the RUST_BACKTRACE setting for std::panic. That's the only in-std API stable that relies on the environment, I believe. (Modulo env::home_dir and env::temp_dir, but those are not so interesting).

@jhpratt
Copy link
Member Author

jhpratt commented Mar 2, 2022

Yeah, get should remain safe. I'll leave that as-is in my PR.

@ghost
Copy link

ghost commented Mar 4, 2022

@Mark-Simulacrum, is the implementation of a deprecated_safe attribute up for grabs? I suggested something like that on IRLO and I've been looking for an excuse to work on Rust. 😊 I've been following the thread but wasn't sure if that was the direction things would be going.

@jhpratt
Copy link
Member Author

jhpratt commented Mar 4, 2022

I'm not him, but to my knowledge no one has worked on such an attribute in any way.

@Mark-Simulacrum
Copy link
Member

Yes, I think so, would be great to get someone to implement it. No guarantees on whether it gets used, of course, but I believe several folks are in favor of using it so chances are good.

@RalfJung
Copy link
Member

RalfJung commented Mar 4, 2022

FWIW we already have at least one example of a "deprecated safe" function: https://doc.rust-lang.org/nightly/std/os/unix/process/trait.CommandExt.html#method.before_exec. That one is just deprecated the normal way now. Not sure if it makes sense to also apply the new approach there.

Certainly if we plan to make an edition change here where calling them as safe functions becomes a hard error, we should also do that for before_exec.

@Mark-Simulacrum
Copy link
Member

Part of the goal on the 'in-place' deprecation is that there's no nice alternative name (before_exec/pre_exec in the CommandExt case), so we're stuck with either using a nice name like env::set, or something with an awkward extra word -- e.g., set_unsafe_var -- and the in-place deprecation leaves the door open to the nice name being used later down the line for a safe API (but one that targets only some use cases, perhaps, like just affecting Rust code, or Rust code and C code using some new libc interfaces, etc.)

@jhpratt
Copy link
Member Author

jhpratt commented Mar 4, 2022

PR introducing the unsafe env::set and env::unset: #94619

@ghost
Copy link

ghost commented Mar 5, 2022

FYI, I've been discussing work on the potential new #[rustc_deprecated_safe] attribute on Zulip here: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/deprecated_safe.20attribute

@ghost
Copy link

ghost commented Mar 7, 2022

I have some commits pushed here that (very, VERY roughly) implement a new #[rustc_deprecated_safe] attribute. I'm hesitant to post even a draft PR as the code quality is not at all up to snuff, but it'd be good know if the approach I'm taking is even valid before I make things proper. I've never touched rustc before, so I'm not really sure what I'm doing. :)

https://github.com/skippy10110/rust/commits/rustc_deprecated_safe

This covers the following places where making set_var unsafe would break existing code, I'm not sure if there are other places that need to be handled:

env::set_var("TEST", "test");

let set_var_fn_ptr: fn(OsString, OsString) = env::set_var;

let set_var_fn_impl: Box<dyn Fn(OsString, OsString)> = Box::new(env::set_var);
let mut set_var_fnmut_impl: Box<dyn FnMut(OsString, OsString)> = Box::new(env::set_var);
let set_var_fnonce_impl: Box<dyn FnOnce(OsString, OsString)> = Box::new(env::set_var);

P.S. I haven't updated the new THIR unsafety checker yet, I figured I'd wait until I knew if any of this code is even correct first.

@JohnCSimon
Copy link
Member

ping from triage:
@jhpratt
Returning to you to build failures

FYI: when a PR is ready for review, send a message containing
@rustbot ready to switch to S-waiting-on-review so the PR is in the reviewer's backlog.

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 8, 2022
@jhpratt
Copy link
Member Author

jhpratt commented May 8, 2022

This is sort of in limbo. Basically there are no technical blockers, but it shouldn't land if #[deprecated_safe] lands. To my knowledge there hasn't been any progress on that front. It's in the same situation as #94619. Given that it's effectively waiting on something else, I'm going to mark it as blocked so that triage doesn't have to keep coming back to it.

@rustbot label -S-waiting-on-author +S-blocked

@rustbot rustbot added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels May 8, 2022
@jhpratt jhpratt closed this Oct 28, 2022
@jhpratt jhpratt deleted the future-deprecate-set_var branch October 28, 2022 04:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.