News for hackers. Stuff that matters.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by woodruffw on Monday October 16, 2023 @09:21PM (#37909111)
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...

[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/s...

Re:Getaddrinfo() on glibc calls getenv(), oh boy by YetAnotherNick on Monday October 16, 2023 @09:32PM (#37909228)
But 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.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by aaronmdjones on Monday October 16, 2023 @09:33PM (#37909239)
It's a library call, not a system call. You're right though, they could implement locking.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by asveikau on Monday October 16, 2023 @09:37PM (#37909272)
I 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.

Re:Getaddrinfo() on glibc calls getenv(), oh boy by Gibbon1 on Tuesday October 17, 2023 @01:07PM (#37918149)
An 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.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by asveikau on Tuesday October 17, 2023 @01:46PM (#37918760)
I 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.

Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Monday October 16, 2023 @10:32PM (#37909799)
That won’t help. The APi is broken. It returns a pointer.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by Sprocklem on Monday October 16, 2023 @11:37PM (#37910271)
The pointer isn't guaranteed to point into `environ` directly. `getenv()` could copy the value to a thread-local, dynamically-allocated buffer while holding the lock.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by alexey-salmin on Monday October 16, 2023 @11:52PM (#37910377)
Call getenv() in a loop and you run out of memory.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by harerazer on Tuesday October 17, 2023 @12:26AM (#37910607)
Then don't do that. Of all the footguns in POSIX/C programming, having to remember to free this is really not as bad as you seem to imply.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by thayne on Tuesday October 17, 2023 @01:14AM (#37910878)
And how would you free it? The current posix API doesn't have any way to reliably free the result returned by `getenv`.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by alexey-salmin on Tuesday October 17, 2023 @04:57AM (#37912275)
You miss the point. If you have full control over when and how getenv is called, there's no issue to begin with. The problem is that you don't, as OP demonstrates. It's perfectly natural to call getaddrinfo in a loop.

We need a new API which is not broken like in NetBSD, and a multi-year migration of all core libraries to it. Well a pity it wasn't started years ago though, could've been 95% done by now.

Re:Getaddrinfo() on glibc calls getenv(), oh boy by Sprocklem on Tuesday October 17, 2023 @12:24AM (#37910599)
I was suggesting that the buffer be invalidated by each subsequent call – like some other libc functions' internal buffers – although, as I noted in the edit this would need `getenv()` to be able to indicate errors (specifically ENOMEM). It currently cannot do this as currently described, because NULL is used to indicate an absent variable.

You could also require callers free the returned memory when they're done, but that would be another change of API.

Re:Getaddrinfo() on glibc calls getenv(), oh boy by alexey-salmin on Tuesday October 17, 2023 @09:12AM (#37914441)
> I was suggesting that the buffer be invalidated by each subsequent call

This would break very simple single-threaded programs that e.g. print two env vars in one printf call.

Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Tuesday October 17, 2023 @12:58PM (#37918026)
The solution to all problems like this was decided years ago: _r

You provide the storage and free it

The problem is these non-direct uses. They each need to switch to •_r and manage the buffer, or offer _r versions themselves and sort of pass through the problem

Re:Getaddrinfo() on glibc calls getenv(), oh boy by Sprocklem on Tuesday October 17, 2023 @05:00PM (#37921496)
Of course, *_r is a better option, but the existing API is used so pervasively that it needs to be made thread-safe to actually avoid thread-unsafe code in, e.g, libraries.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Tuesday October 17, 2023 @11:44PM (#37924638)
I don’t see how you can make:

- return a pointer - the library owns the allocation - the state is global and mutable

Thread safe

Re:Getaddrinfo() on glibc calls getenv(), oh boy by Sprocklem on Wednesday October 18, 2023 @07:54AM (#37927389)
A number of libc functions return a pointer to an internal thread-local buffer, which is invalidated on subsequent calls. If the function copies the environment variable's value to such a buffer while holding the mutex controlling access to the global state, then the returned value is guaranteed to remain unaffected by other threads.

There are, however, other problems (discussed elsewhere in this thread) that complicate such an API in the context of getenv().

Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Wednesday October 18, 2023 @11:16AM (#37929884)
Requiring you to hold a mutex to safely call the function is an API change
Re:Getaddrinfo() on glibc calls getenv(), oh boy by Sprocklem on Wednesday October 18, 2023 @03:47PM (#37933817)
I was not suggesting the caller hold a mutex, but rather getenv(), which eould be transparent to the caller.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Thursday October 19, 2023 @08:37PM (#37950759)
Don’t you need C++11?
Re:Getaddrinfo() on glibc calls getenv(), oh boy by lokar on Tuesday October 17, 2023 @11:05AM (#37916283)
Libc and much of posix predate threads. There was no way to fix everything without changing the APIs, which they did in many cases but not all.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by cryptonector on Tuesday October 17, 2023 @02:22AM (#37911290)
It should never use locks. Solaris/Illumos' getenv() is lock-less. Every C library should copy that pattern.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by Retr0id on Monday October 16, 2023 @09:28PM (#37909189)
I wonder if you could get around this by giving each thread its own environment context, and synchronizing them, asynchronously.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by gpderetta on Tuesday October 17, 2023 @08:32AM (#37913951)
Just 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.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by tinus_hn on Tuesday October 17, 2023 @09:08AM (#37914387)
Because 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.
Re:Getaddrinfo() on glibc calls getenv(), oh boy by jrmg on Monday October 16, 2023 @09:28PM (#37909184)
That 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.