5 ms·
I think the missing piece here is how POSIX specifiers the environment: `getenv(3)` and `setenv(3)` are accessors for `environ`[1], which is just a pointer to s
by woodruffw 3y ago
I think the missing piece here is how POSIX specifiers the environment: `getenv(3)` and `setenv(3)` are accessors for `environ`[1], which is just a pointer to some memory (over which walking is also specified[2]). That level of granularity means that it's pretty hard to add any locking here.
[1]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1...
[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/s...
- Retr0id 3y agoI wonder if you could get around this by giving each thread its own environment context, and synchronizing them, asynchronously.
- deleted 3y ago[deleted]
- gpderetta 3y agoJust independent environments would be the best, like it was done for locale with uselocale. But it is a breaking change, would have to go through posix and will take forever anyway. Also, as environ is a public symbol, it has ABI implications.
- tinus_hn 3y agoBecause there is no way to tell when a thread is done with the buffer, there is no moment when you can be sure you can manipulate it. The options are create a new copy and leak the old one or accept the chance of a segfault.
- jrmg 3y agoThat makes a lot of sense. You’d need to snapshot environ when the lock was taken (when another thread could be accessing it!), which I imagine would be complicated. Although surely possible.
- YetAnotherNick 3y agoBut getenv/setenv syscall could still be under lock, which I think most of the people would use. Walking over memory could be without lock and the program could see inconsistent values as is the current behaviour.
- aaronmdjones 3y agoIt's a library call, not a system call. You're right though, they could implement locking.
- asveikau 3y agoI think in libc, there's a lot of stuff where an interface is kind of broken from a thread perspective, and it could be implemented better without changing the interface, but often people generally do not. I can't think of any examples offhand, but I often think it about thread-local storage. Eg. lots of interfaces have an _r() equivalent where you provide the buffer, but many people still call the unsafe one which is broken when there are threads... In my mind, the best way to do this would be to use static thread-local storage in the non-_r() one, and have it call the _r() one ... Sure that has overhead and isn't a perfect solution, but it's better than "bad". But a lot of these old functions don't necessarily get love.
- Gibbon1 3y agoAn a sane world creating a thread would set a global that causes non thread safe library functions to seg fault. Or maybe calling them from two different threads causes a seg fault. But just make it really obvious you're doing bad stuff.
- asveikau 3y agoI think that sounds completely insane. The unsafety is an emergent property of what's done in the function, and entirely dependent on usage. If you were doing very disciplined use of an unsafe call, it's harmless. Perhaps this would be a good feature of an assert, or something that breaks in a debugger if it's attached. But I don't think that is reasonable for production.