16 ms·
C stdlib isn't threadsafe and even safe Rust didn't save us
- jandrese 2y agoYet another person is burned by calling setenv() in a multi-threaded context. There really needs to be a big warning banner on the manpage for setenv() that warns about this because it seems like a far more common problem than you would expect.
- 01HNNWZ0MV43FF 2y agoFunny enough, the Rust wrapper `std::env::set_var` does have a big warning https://doc.rust-lang.org/std/env/fn.set_var.html https://doc.rust-lang.org/std/env/fn.set_var.html
- subarctic 2y agoLooks like that Safety section was added in 1.76.0. It'll be an even bigger warning in the future since it's now going to be unsafe in Rust 2024
- umpalumpaaa 2y agoThe man page says: > POSIX.1 does not require setenv() or unsetenv() to be reentrant. A non-reentrant function cannot be thread safe. In general (for POSIX, libc and many other libraries: if the docs do not explicitly say "this function is thread safe" they are not).
- wmf 2y agoIt's time to move beyond this attitude and make things safe by default. For example, Solaris has a safer version of setenv(). "It is ridiculous that this has been a known problem for so long. It has wasted thousands of hours of people's time, either debugging the problems, or debating what to do about it. We know how to fix the problem." https://www.evanjones.ca/setenv-is-not-thread-safe.html https://www.evanjones.ca/setenv-is-not-thread-safe.html
- umpalumpaaa 2y agoI am not sure making things safe by default is a good idea. This always comes with a cost. Thats also the reason why basic data types (array, dictionaries, etc) are generally not thread safe… because its usually not needed or handled on a much higher level. Its a different story for languages/environments that are supposed to be safe by default and where you have language features that ensure safety (actors, optionals etc) but not for something like libc which has a standard it has to conform to and like 100 years of history.
- dgrunwald 2y agoThe problem with `setenv` is that people expect one process to have one set of environment variables, which is shared across multiple languages running in that process. This implies every language must let its environment variables be managed by a central language-independent library -- and on POSIX systems, that's libc. So if libc refuses to provide thread-safety, that impacts not just C, but all possible languages (except for those that cannot call into C-libraries; as those don't need to bother synchronizing the environment with libc).
- PaulDavisThe1st 2y agoIt's not just that "libc refuses to provide thread-safety" ... the POSIX standard specifies that these functions are non-reentrant.
- saagarjha 2y agoA conformant implementation can make a non-reentrant function actually safe under the hood for people that call into it erroneously. Unfortunately, there is no way to do this for getenv/setenv, because of the API they expose (specifically, when environ is accessed directly).
- tsimionescu 2y agoIn some cases this is true. In the case of setting and getting env vars, it is not. There is no comceivable reason for making a process that spends any significant portion of its runtime calling setenv() or getenv(). Even if those calls were a thousand times slower than today, it would still be a non-issue.
- deleted 2y ago[deleted]
- jabl 2y ago> A non-reentrant function cannot be thread safe. Actually, a non-reentrant function can be thread-safe. A common example of such a function in libc being malloc().
- adrian_b 2y agoBy definition, a "reentrant function" is a function that may be invoked even when it has not returned yet from a previous invocation. So a non-reentrant function is a function that may not be invoked again between a previous invocation and returning from that invocation. When a function may be invoked from different threads, then it is certain that sometimes it will be invoked by a thread before returning from a previous invocation from a different thread. Therefore any function that may be invoked from different threads must be reentrant. Otherwise the behavior of the program is unpredictable. Reentrant functions may be required even in single-thread programs, when they may be invoked recursively, or they may be invoked by signal handlers. An implementation of "malloc" may be reentrant or it may be non-reentrant. Old "malloc" implementations were usually non-reentrant because they used global variables for managing the heap. Such "malloc" functions could not be used in multi-threaded programs. Modern "malloc" implementations are reentrant, either by using only thread-local storage or by using shared global variables to which some method for concurrent access is implemented, e.g. with mutual exclusion.
- tedunangst 2y agoWho has a signal safe malloc?
- adrian_b 2y agoPOSIX does not require malloc to be signal safe. Therefore I do not think that anyone has bothered to implement a signal-safe malloc, as this is likely to be complicated. Allocating memory in a signal handler makes no sense in a well designed program, so not being allowed to use malloc and related functions is not a problem.
- 2y ago
- forrestthewoods 2y agoMutable global state is evil. Friends don’t let friends use mutable global state. I hate envvars. It’s “the Linux way”. I avoid them like the plague. A++ strong recommend. libc is terrible. The world needs to move on.
- 01HNNWZ0MV43FF 2y agoEnv vars are good if you treat them as read-only within the process
- msully4321 2y agoYeah, setenv should probably just not exist, and environment variables should be only set when spawning new processes.
- plorkyeran 2y agoThe problem is that applications sometimes need to set environment variables which will be read by libraries in the same process. This is safe to do during startup, but at no later times. Ideally all libraries which use environment variables should have APIs allowing you to override the env variables without calling setenv(), but that isn't always the case.
- msully4321 2y agoYeah, the cows have certainly gotten out already.
- docandrew 2y agoI’d argue that libraries shouldn’t read environment variables at all. They’re passed on the initial program stack and look just like stack vars, so the issue here is essentially the same as taking the address of a stack variable and misusing it. Just like a library wouldn’t try to use argv directly, it shouldn’t use envp either (even if done via getenv/setenv)
- 2y ago
- ChrisSD 2y agoIn the Rust std, `set_var` and `remove_var` will correctly require using an `unsafe {}` block in the next edition (2024). The documentation does now mention the safety issue but obviously it was a mistake to make these functions safe originally (albeit a mistake even higher level languages have made). https://doc.rust-lang.org/stable/std/env/fn.set_var.html https://doc.rust-lang.org/stable/std/env/fn.set_var.html There is a patch for glibc which makes `getenv` safe in more cases where the environment is modified but C still allows direct access to the environ so it can't be completely safe in the face of modification https://github.com/bminor/glibc/commit/7a61e7f557a97ab597d6fca5e2d1f13f65685c61 https://github.com/bminor/glibc/commit/7a61e7f557a97ab597d6f...
- Thaxll 2y agoWhy requiring unsafe when the std implementation could take care of the synchronisation?
- demurgos 2y agoIt can't ensure synchronization because any code using libc could bypass the sync wrapper. In particular, Rust lets you link C libs which wouldn't use the Rust stdlib.
- msully4321 2y agoBecause it can still race with C code using the standard library. getenv calls are common in C libraries; the call to getenv in this post was inside of strerror.
- ChrisSD 2y agoIt can only synchronize if everything using is Rust's functions. But that's not a given. People can use C libraries (especially libc) which won't be aware of Rust's locks. Or they could even use a high level runtime with its own locking but then they'll be distinct from Rust's locks. The only way to coordinate locking would be to do so in libc itself.
- wahern 2y ago
- mmastrac 2y agoThe major takeaway from this is that Rust will be making environment setters unsafe in the next edition. With luck, this will filter down into crates that trigger these crashes (https://github.com/alexcrichton/openssl-probe/issues/30 https://github.com/alexcrichton/openssl-probe/issues/30 filed upstream in the meantime).
- benatkin 2y agoPeople get trained to ignore the ____UNSAFE_payattention__nevermindthatthisappears50timesinthisfile___ blocks and prefixes This also shows up in web frameworks where Vue has the v-html directive and react has dangerouslySetInnerHTML. Vue definitely has it better.
- crooked-v 2y agoIn the React world, the only times I've seen dangerouslySetInnerHTML consistently used is for outputting string literal CSS content (and this one is increasingly rare as build tools need less handholding), string literal JSON content (for JSON+LD), and string literal premade scripts (i.e. pixel tags from the marketing content). That's not to say there's no danger surface there, but it's not broadly used as a tool outside of code that's either really bad or really exhaustively hand-tuned.
- benatkin 2y agoReact doesn't have a tag and attribute sanitizer built in, so having non-js-programmers edit JSX isn't especially safe anyways, as an img or a href could exfiltrate data. If it were they could just block out an innerHTML attribute. A js programmer can get around it by setting up a ref and then using the reference to set innerHTML without the word dangerously appearing.
- koito17 2y ago> A js programmer can get around it by setting up a ref and then using the reference to set innerHTML without the word dangerously appearing. If DOM nodes during the next render differ from what react-dom expects (i.e. the DOM nodes from the previous render), then react-dom may throw a DOMException. Mutating innerHTML via a ref may violate React's invariants, and the library correctly throws an error when programmers, browser extensions, etc. mutate the DOM such that a node's parent unexpectedly changes. There are workarounds[1] to mutate DOM nodes managed by React and avoid DOMExceptions, but I haven't worked on a codebase where anything like this was necessary. [1] https://github.com/facebook/react/issues/11538#issuecomment-390386520 https://github.com/facebook/react/issues/11538#issuecomment-...
- masklinn 2y agoPreviously on setenv being a terrible thing: https://www.evanjones.ca/setenv-is-not-thread-safe.html https://www.evanjones.ca/setenv-is-not-thread-safe.html (discussion: https://news.ycombinator.com/item?id=38342642 https://news.ycombinator.com/item?id=38342642 first comment is even about it causing issues in Rust)
- Animats 2y agoYes. That's known. Most of the rest of the problem here seems to be the development environment. They're testing on a remote machine in an Amazon data center and using Docker. This rig fails to report that a process has crashed. Then they don't have enough debug symbol info inside their container to get a backtrace. If they'd gotten a clean backtrace reported on the first failure, this would have been obvious. Why is anyone using "setenv" anyway?
- masklinn 2y ago> Why is anyone using "setenv" anyway? Because it’s there and it looks like a good idea until it takes one of your fingers.
- einpoklum 2y agoIt really does not look like a good idea to setenv() . The very notion is quite terrifying. Messing with a bunch of globals, that other code knows about as well? Nuh-uh. The thing is, the OP people weren't doing that at all, it was some irresponsible library maintainers. If your code does that, you have to include something like the "surgeon general's warning" everywhere: "CAREFUL: USING THIS LIBRARY MAY CAUSE TERMINAL CRASHES".
- SAI_Peregrinus 2y agoIt's OpenSSL. It's basically a sea urchin turned into code in terms of safe handling.
- Animats 2y ago
- shikon7 2y agoI wonder why it is so hard for Rust to implement its own safe stdlib independent of C.
- zanderwohl 2y agoIt would be a tremendous amount of work, and would take years. Meanwhile, the problems are avoidable. It's not exactly the "rust way" to just remember and avoid problems, but everything in language design is compromises.
- IshKebab 2y ago"Impossibru!!" https://github.com/sunfishcode/eyra https://github.com/sunfishcode/eyra Oh look: > Why use Eyra? It fixes Rust's set_var unsoundness issue. The environment-variable implementation leaks memory internally (it is optional, but enabled by default), so setenv etc. are thread-safe.
- kbolino 2y agoThat's quite a trade-off
- IshKebab 2y agoWhat is? Leaking memory? It's going to be a few kB at absolute most. Not an issue unless you are doing something very weird.
- mmastrac 2y agoI think glibc made the same trade-off. It makes sense for most types of programs, but there's certainly a lot of classes of programs that wouldn't take it.
- sunshowers 2y agoThat only works on Linux though right?
- 2y ago
- lopkeny12ko 2y agoThe whole point of Rust is memory safety, not thread safety...
- masklinn 2y agoRust literally bakes data race safety into the language. While it does not resolve general race conditions, thread safety issues which cause memory unsafety (which an UAF or dangling pointer would be) are very much within its remit.
- vlovich123 2y agoEven if C stdlib maintainers are resistant against making setenv multi-thread safe, at a minimum there should be a new alternative thread-safe API defined, whether within POSIX or defining a defacto standard and forcing POSIX to adopt it over time. If instead of explaining why nothing could be done was spent fixing this problem, a new thread-safe API could have replaced the old setenv which could have been deprecated and removed from many software projects. I'm also not convinced by Musl's maintainer that it can't be fixed within Musl considering glibc is making changes to make this a non-issue.
- panzi 2y agoGuess that would also require some locking for all the exec() functions that don't take the environment as a parameter or that search PATH for the executable.
- usefulcat 2y agoThe biggest problem is not the absence of a thread safe API, it's the existence of this: extern char **environ; As long as environ is publicly accessible, there's no guarantee that setenv and getenv will be used at all, since they're not necessary. If you're willing to get rid of environ, it's pretty trivial to make setenv and getenv thread safe. If not, then it's impossible, although one could still argue that making setenv and getenv thread safe is at least an improvement, even if it's not a complete solution (aka don't let the perfect be the enemy of the good).
- vlovich123 2y ago> aka don't let the perfect be the enemy of the good Exactly my point. Over time *environ would disappear, at least from the major software projects that everyone uses (assuming it's even in use in them in the first place).
- IshKebab 2y agoYeah I don't think I've ever seen a single use of it. However I just checked on grep.app and at least a few big softwares use it - git, nginx, Postgresql, neovim, etc, which suggests that setenv/getenv is not sufficient.
- datadeft 2y agoCouldn't we have a better pattern for this? if (__environ == NULL || name[0] == '\0') return NULL;
- StillBored 2y agoIts like a rite of passage to be hit by an environment related bug on linux, which is mysteriously less a problem on other unix's. Which is sorta funny given how pragmatic Linus and the kernel are about fixing POSIX bugs by making them not happen, while glibc is still lagging here decades after people tried to at least make the problem better. Sure there is all the crap around TZ/etc, but simply providing getenv_r() and synchronizing it with setenv() and warning during compile/link on getenv() would have killed much of the problem. Nevermind, actually doing a COW style system where the env pointer(s) are read only. Instead the problem is pushed to the individual application, which is a huge mistake, because application writers are rarely aware of what their dependencies are doing. Which is the situation I found myself in many many years ago. The closed source library vendor, at the time, told us to stop using that toy unix clone (linux).
- kelnos 2y ago> environment related bug on linux, which is mysteriously less a problem on other unix's. How do you figure? The problem isn't the implementation, it's the API. setenv(), unsetenv(), putenv(), and especially environ, are inherently unsafe in a multithreaded program. Even getenv_r() can't really save you, since another thread may be calling setenv() while the (old) value of an env var is being copied into the provided buffer. Sure, a getenv_r() fixes the case where you get something back from getenv(), and then another thread calls setenv() and makes that memory invalid, but there's no way to protect the other calls breaking the API. There are ways to mitigate some of the issues, like having libc hold a mutex when inside getenv()/setenv()/putenv()/unsetenv(), but there's still no way for libc to guarantee that something returned by getenv() remains valid long enough for the calling code to use it (which, right, can be fixed by getenv_r(), which could also be protected by that mutex). But there's no good way to make direct access to environ safe. I suppose you could make environ a thread-local, but then different threads' views of the environment could become out of sync, permanently (and you could get different results between calling getenv_r() and examining environ directly). Back-compat here is just really hard to do. Even adding a mutex to protect those functions could change the semantics enough to break existing programs. (Arguably they're already broken in that case, but still...)
- deleted 2y ago[deleted]
- gavinhoward 2y agoIt is weird that I got this right before Rust did. Because I use structured concurrency, I can make it so every thread has its own environment stack. To add to a new environment, I duplicate it, add the new variable, and push the new enviroment on the stack. Then I can use code blocks to delimit where that stack should be popped. [1] This is all perfectly safe, no `unsafe` required, and can even extend to other things like the current working directory. [2] IMO, Rust got this wrong 10 years ago when Leakpocalypse broke. [3] [1]: https://git.yzena.com/Yzena/Yc/src/branch/master/tests/yao/env.yao https://git.yzena.com/Yzena/Yc/src/branch/master/tests/yao/e... [2]: https://gavinhoward.com/2024/09/rewriting-rust-a-response/#global-context https://gavinhoward.com/2024/09/rewriting-rust-a-response/#g... [3]: https://gavinhoward.com/2024/05/what-rust-got-wrong-on-formal-verification/ https://gavinhoward.com/2024/05/what-rust-got-wrong-on-forma...
- mmastrac 2y agoThis isn't _really_ a Rust problem. Rust is a victim of POSIX. If you have 1) C FFI interop in Yao, there's still a chance you might have two C libraries cause a crash without your code even being involved.
- gavinhoward 2y agoExcept if there is dymanic linking, I can use that to inject my own setenv and getenv, just like people inject jemalloc or other malloc alternatives.
- deleted 2y ago[deleted]
- wakawaka28 2y agoSounds like you just didn't know it's not threadsafe. This is common knowledge in the C and C++ world.
- hauntsaninja 2y agoWe had so many of these issues that we ended up LD_PRELOAD-ing patch getenv / setenv / putenv
- msully4321 2y agoWith a fixed implementation that leaks environments (like the one that just landed in glibc)?
- kelnos 2y agoThis reminded me of that whole "12-factor app" movement, which several of my former coworkers had really bought into. One of the "factors" is that apps should be configured by environment variables. I always thought this was kinda foolish: your configuration method is a flat-namespace basked of stringly-typed values. The perils of getenv()/setenv()/environ are also, I think, a great argument against using env vars for configuration. Sure, there aren't always great, well-supported options out there. I prefer using a configuration file (you can have templated config and a system that fills in different values for e.g. dev/stage/prod), and I'll usually use YAML, despite its faults and gotchas. There are probably better configuration file formats, but IMO YAML is still significantly better than using env vars.
- eqvinox 2y agogetenv() is perfectly fine, it's setenv() that is the problem. Which in theory this wouldn't be using since the env would be set up prior to starting that mystical app. But yes, a flat namespace, with string values, shared as a free-for-all with who knows what libraries and modules you're loading… that's not a good idea even if it didn't have safety issues in setenv().
- __MatrixMan__ 2y agoI have similar reservations about env vars. I dislike how they can be read from anywhere--it interrupts the ability to reason about a function's behavior from its signature and makes impure plenty of functions that could otherwise have been pure. If there were a language feature that let me mark apps such that during any process env vars are not writable and are readable only once (together, in a batch, not once per var), I'd use it everywhere.
- johnny22 2y agoThis is unrelated really. If you read your enviornment variables into config and never touched them again, then you're totally safe. I personally use 12 factor app style, but once it's entered the app I validate the env variables and data and then store them. It's totally fine after that.
- jillesvangurp 2y ago
- cuno 2y agoWe ended up overriding and replacing with our own thread-safe version years ago when we also hit this.
- einpoklum 2y agoA function which sets global process state is not thread safe? Why, I'm shocked; shocked and chagrined. But really, I don't understand why a sensitive security-related library would implicitly use an unsafe function like setenv().
- bangaladore 2y ago> A function which sets global process state is not thread safe? Why, I'm shocked; shocked and chagrined. This is a oversimplification. Windows has essentially the exact same API and it works just fine in multithreaded contexts. The issue here is unix allows the underlying pointer to be accessed, bypassing any possible thread-safe APIs.
- HarHarVeryFunny 2y agoWhat is the rationale for libc not making setenv/getenv thread safe? It does seem rather odd given how environment variables are explicitly defined as shared between threads in the same process! It doesn't seem it would take much to do it efficiently, even retaining the poor getenv() pointer-returning API (which could point to a thread local buffer). The coordination between getenv and setenv could be very lightweight - spinlock vs mutex.
- 4gotunameagain 2y agoI think the argument was that the standard states that setenv is not thread safe, although from what I see it says that it does not have to be thread safe: The setenv( ) function need not be thread-safe. A function that is not required to be thread-safe is not required to be reentrant. https://www.open-std.org/jtc1/sc22/open/n4217.pdf https://www.open-std.org/jtc1/sc22/open/n4217.pdf. Page.. 1860 :')
- HarHarVeryFunny 2y agoSure, but given that Linux defines the environment as state that's shared between threads, not having a thread-safe way of accessing it is hard to defend... Is "the standard says it doesn't NEED to be thread safe" the argument that the Linux libc maintainers are using for not enhancing it to be thread safe, or is it based on some technical or backwards compatibility issues in doing so ?
- debugnik 2y agoThe only thread-safe way to implement getenv/setenv as they currently exist is to leak the previous state when setenv allocates, such that existing pointers stay valid. The existing API simply lacks a mechanism to synchronize correctly. Leaking would be good enough for many use cases, but it would break long-running users of setenv (mainly those with libraries abusing env vars, as in TFA), and doesn't even solve how they interact with putenv and environ. This whole API is just cursed. Libc could of course get better APIs, like GetEnvironmentVariable on Windows, but that won't fix all existing code.
- rikthevik 2y agoGreat article about digging into a non-obvious bug. This one had it all! Intermittent bug, architecture-specific, hidden in a dependency, rust, the python GIL, gettext. Fantastic stuff. These kinds of detailed troubleshooting reports are the closest thing you can get to having to do it yourself. Thanks to the authors. It's easy to say "don't use X duh" until a dependency relies on it, and how were you supposed to know?
- vrtx0 2y agoLet me try to help: 1. If a process crashes and dumps, be sure to look at the system log of the cause (e.g. SIGSEGV, OOM, invalid instruction, etc.) 2. Be certain you’re looking at the right core dumps — I believe UID 1000 just means posix UserID (which is unrelated to a PID), though I don’t use containers. 3. Stay focused on the right level of abstraction — memory model details are great to know, but irrelevant here. 4. Variables do not correlate 1:1 with registers, except in C calling conventions. The assumption about x20 and a local variable is incorrect, unfortunately. 5. getenv() and setenv() do not work as implied in the post. When a process starts via execve(), the OS/libc constructs a new snapshot of the environment, and cannot be modified by an ancestral process. It’s a snapshot in time, unless updated by the process itself. When a process fork()s, the child gets a new copy of the parent’s environment — updates do not propagate. getenv() is thread safe and reentrant. You don’t use an environment to pass shared data — setenv() is generally used when constructing the environment for a child process before a fork(). See man environment. 6. FWIW, ‘char** env’ is a null-terminated array of pointers, so dumping memory from *env (or env[0]) is only valid until you hit the first NULL. The size of the array is not stored in the array. I hope this helps! And apologies if this is redundant — I read so many comments; mostly variations of “the problem with getenv is x”, but gave up before reading all of the (currently) 168 comments.
- saagarjha 2y agoI'm kind of confused by this response. It doesn't seem to match the actual article? For example, they consulted the code to find what x20 had in it, rather than blindly guessing. Doing that is perfectly fine and even desirable when analyzing crashes. There is no forking mentioned. People call setenv all the time when trying to modify their own environment (hence the crashes!). Nobody said anything about the size of env.
- vrtx0 2y agox20 is a general purpose register; optimizing compilers can use it for any number of variables, immediate values or intermediate computations at different points within that same function — or none at all (the variable ep could be optimized away). Re: fork(), I just meant to be thorough in explaining the environment is copied, not shared by processes. Setenv() only affects the process from which it’s called. The array size bit in the article: The value 0x220 looks suspiciously close to the size of the old environment in 64-bit words (0x220 / 8 = 68), and this value was written over the terminating NULL of the environment block… HTH!
- throwaway2037 2y agoClick bait title? GLibC is very clear about what is and what is not thread-safe. I looked at the article: They fell victim to the classic getenv()/setenv() trap. This has been blogged about many times. If you look at the man page for setenv(): Ref: https://man7.org/linux/man-pages/man3/setenv.3.html https://man7.org/linux/man-pages/man3/setenv.3.html ... it clearly says: "MT-Unsafe" Also, there is a whole section about get/set env thread safety here (under "Other safety remarks -> env"): https://man7.org/linux/man-pages/man7/attributes.7.html https://man7.org/linux/man-pages/man7/attributes.7.html
- roca 2y agoSwitching from OpenSSL to rustls solves even more problems than expected.
- nwellnhof 2y ago> Our nightly CI machines run on Amazon AWS, which has the advantage of giving us a real, uncontainerized root user. > We don’t have the necessary files outside of the container, and our containers are quite minimal and don’t allow us to easily install gdb. Have people lost the ability to build and debug their code locally, without clouds and containers?
- msully4321 2y ago> Have people lost the ability to build and debug their code locally, without clouds and containers? No, of course not, but it didn't crash on our machines!
- api 2y agoYes. It’s shocking just how much cloud SaaS has distorted peoples understanding of things. You need all kinds of layers of cloud complexity and deployment to do the most trivial stuff. We have 100% reversed the PC revolution and returned to the era of clunky expensive mainframe computing. The reason is that cloud is where all the money is because cloud is DRM. Put software there and you can charge a subscription and nobody can evade it and you have perfect lock in forever. People usually can’t even get their data out. You can also do all kinds of realtime analytics conveniently to optimize your product. Computing architecture is downstream of the business model. Mainframe died originally because there was no Internet and PCs were cheaper, but vendors also lost a lot of their lock in power. Now they have a way to bring a model that is much more profitable back. No more pesky freedom for users, who to be fair if given such freedom will often just refuse to pay, making quality software a non-viable business. Tangent I know.
- Meneth 2y agoFrom the backtrace, it seems strerror_r is not thread-safe, since it calls __dcigettext which calls getenv. A similar bug related to setlocale was found in 2007 and fixed in 2014. That bug did not take getenv/setenv into account. https://sourceware.org/bugzilla/show_bug.cgi?id=5443 https://sourceware.org/bugzilla/show_bug.cgi?id=5443
- janmatejka 2y agoThis reminds of the time I was not able to get setproctitle to work in certain code base. Eventually I narrowed the issue to this line: import numpy setproctitle() worked before numpy import but not after because it couldn't find the memory address of **environ. I'm hazy on the details but it led me to a somethingenv call (possibly getenv or setenv) in numpy initialization and it turned out that function changed the address of **environ and that was the reason for why setproctitle couldn't find it.
- colonial 2y agoTIL that my set_env("RUST_LOG"...) calls at startup are technically unsafe. Funny. I should see if the env_logger crate has a better solution.
- loeg 2y agoenv::set_var is marked unsafe now: https://doc.rust-lang.org/std/env/fn.set_var.html https://doc.rust-lang.org/std/env/fn.set_var.html And: > This function is safe to call in a single-threaded program. > This function is also always safe to call on Windows, in single-threaded and multi-threaded programs. > In multi-threaded programs on other operating systems, the only safe option is to not use set_var or remove_var at all.
- kazinator 2y agoThis is not just a thread issue! You run into a problem if you keep using a string returned by getenv after calling another environment function: including possibly getenv itself! However, it's easy to just strdup the result of getenv; that defends against the issue in a single-threaded program.
- up2isomorphism 2y agoDoes posix say setenv us thread safe? If not, why complain about it?