5 ms·
You can't. You could wrap setenv in a mutex, but that's not good enough. It can still be called from different processes, which means you'd need to do a more
by cogman10 2y ago
You can't.
You could wrap setenv in a mutex, but that's not good enough. It can still be called from different processes, which means you'd need to do a more expensive and complex syncing system to make it safe.
That ballons out to other env related methods needing to honor the synchronization primitive in order for there to be a semblance of safety.
However, you still end up in a scenario where you can call
setenv
getenv
and that would be incorrect because between the set and the get, even with mutexes properly in place and coordinated amongst different applications, you have a race condition where your set can be overwritten by another application's set before your get can run. Now, instead of actually making these functions safe you've buried the fact that external processes (or your own threads) can mess with env state.
The solution is to stop using env as some sort of global variable and instead treat it as a constant when the application starts. Using setenv should be mostly discouraged because of these issues.
- ryao 2y agoHow does an external process mess with env state? As far as I know, you pass the environment when doing the execvpe() and then you cannot touch it from outside of the process anymore.
- jenadine 2y agoYou're correct. Parent comment is inaccurate. The problem is that a different library in the same process can use getenv without locking (or without locking the same lock as your code)
- tsimionescu 2y agoOf course you can. Mutexes are system objects, so it's not a huge problem to sync across processes, if you really have to (is it really expected that one process can set env vars inside another process?). Making global state, especially state that has no reason to be modified or even read very often like the env, thread safe is a trivial issue, well studied and understood. Could an intern do it? Probably not. Could literally any maintainer of a standard C library? Easily. This is much more of a culture problem preventing such obvious flaws from being recognized as such. Side-note: your set-then-get example is a theoretical problem in search of a use case. Why would you ever want to concurrently set an env var and expect to be guaranteed to read that same value? And even if this is a real thing that applications really use, exposing a new function to sync anything on the env mutex is, again, trivial. So, if you really needed that, you could do lockenv setenv getenv unlockenv And problem solved.
- kelnos 2y agoThat doesn't solve anything. You could be using a library (perhaps a closed-source one) that doesn't use these hypothetical lockenv()/unlockenv() functions. This needs to be fixed inside libc, but there's no way to do so completely without breaking backward-compatibility.
- tsimionescu 2y agoYes, I was talking about fixes inside libc. The poster above was claiming it can't be done inside libc. And the lockvenv/unlockvenv functions I was mentioning were meant to exist besides the internal locking inside setenv/getenv. They would only be used if you needed transactional access (a combination of setting/getting multiple env vars atomically).
- xxs 2y agousing copy on write would be easier (and more performant), along with getenv_r. POSIX requires not to copy the data which makes the entire mutex/lock or CoW pointless. Of course, there will be the mandatory mentioning of "extern char *environ;"[0]. That returns are raw C strings as you can find them. What could work is per thread env. changes - but that's not likely to happen [0]: https://linux.die.net/man/3/environ https://linux.die.net/man/3/environ
- sunshowers 2y agoThat is a technical solution. What is your solution to the much more serious social problem of adding this check to every codebase in existence? What points of leverage do you have?
- tsimionescu 2y agoThe point was about adding a mutex inside libc in getenv and setenv. That way, every codebase in existence automatically gets this safety. The poster I was replying to claimed that this wouldn't help, because it would still not offer thread safety when doing multiple operations. I pointed out that, in addition to libc setenv/getenv using a mutex internally, they could also expose new functions to allow transactional access for anyone that really needs it - though I suspect that is a vanishingly small minority.
- wmf 2y agoYou didn't read the link, did you?