3 ms·
The problem isn't setenv() so much as getenv returns raw pointers to the data structure that is potentially being manipulated. But this is/was strictly a glibc
by StillBored 3y ago
The problem isn't setenv() so much as getenv returns raw pointers to the data structure that is potentially being manipulated.
But this is/was strictly a glibc/Linux bug because maintainers in the past didn't want to improve the situation (read add locks which weren't 100% reliable) nor add thread safe version of the calls: ex getenv_s/getenv_r as pretty much every other POSIX compliant system has done.
And so the situation I hit many years ago was a proprietary library doing setenv's before fork() has now been fixed (and me calling those routines from multiple threads), and setenv() on linux/glibc is now working if it's built with locking:
https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/setenv.c;h=cc71287fcc2726701ac13cbfc402979e06a0d29d;hb=HEAD#l133 https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/sete...
So the remaining issue is making the environment thread safe, which means adding a getenv_r/s call and assuring its being used everywhere, which is probably a more complex problem than tossing the setenv() lock. But then in the setenv/fork case the forked process is a crapshoot whether it gets the "right" environment. In my case above it didn't really matter because the library was doing the equivalent of `export YOURACHILD=1` so the value wasn't being changed from invocation to invocation.
But there are dozens and dozens of other similar gochas in the spec, where error conditions or threading races exist and aren't noticeable until one understands how it is being implemented. There isn't a way to fix it with the library itself because many of the calls need more stored context space (ala win32 handles). So one ends up doing things like building serialization locks into the application to assure certain subsets of the posix/c/etc libraries aren't being called in parallel. (in this case getenv/setenv/fork).