Unsafe std::env::set_var change

It is in POSIX terms MT-unsafe, so you can cause a segfault or other undefined behavior calling it in a multi-threaded context.[1] You're not supposed to be able to cause UB in Rust without using unsafe, so this is a soundness fix.

The core idea[2] is that the environment should be basically read-only; there may be a period where you know you're the only thread and set up the environment for everything else. During such a period it's up to you to ensure there's nothing else around that might call getenv or similar (which is effectively allowed to happen at any time, and does, because e.g. random libraries read the environment unannounced to look for FROBLIB_BEHAVIOR_HACK=17).[3]

One may ask, are their other possible fixes besides making the function unsafe? And indeed, that question was asked and various alternatives explored.

  • Add environment locking in std?
    • Already present, actually, but there's no way to force others to use it; particularly C libraries and even other system calls (localtime, getaddrinfo, ...)
  • Clone the environment in every program as part of the Rust runtime (Rust shadow environment)?
    • Breaks backwards compatibility as now there are two environments; similar problems in that you can't force everyone to use the Rust environment; opposition to the existence of a shadow environment at all; adds to startup cost even if you don't need it
  • Detect threads and panic or something?
    • No guaranteed way to do this, false-positive panics are undesirable and may make set_env useless for some use cases, also makes set_env useless if you know you have an inert thread, arguments that you need "was never another thread", ...
  • Change POSIX (and wait for "everything" to catch up)
    • Good luck and we're talking decades even if it would fly

Here's a good starting point if you want to read more.

TL;DR it's not a frivolous or cavalier change, and has been hashed out over the course of years.


  1. File ops in contrast can't cause this. ↩︎

  2. outside of Rust's control ↩︎

  3. Or... RUST_BACKTRACE ↩︎

21 Likes