Live data from Hacker News

C stdlib isn't threadsafe and even safe Rust didn't save us

edgedb.com

331–340 of 370 posts

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#331
post #326
post #296

Earlier quoted context omitted.

The p in pthreads stands for Posix. I.e., uh, Posix is neither set in stone, nor entirely predates threads.

I am old enough to remeber when UNIX only had processes, and several thread designs were being discussed until eventually pthreads one design won. POSIX predates adoption of threads in the UNIX world.

Yes, yes, but also post-dates adoption of threads. It's an ongoing standard.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#332
post #319
post #296

Earlier quoted context omitted.

The p in pthreads stands for Posix. I.e., uh, Posix is neither set in stone, nor entirely predates threads.

And when can we expect the version of Posix that fixes setenv to be MT-safe?

You've shifted the goalposts from the comment I responded to.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#333

Earlier quoted context omitted.

It 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 TERMIN…

Sadly, it's often the only way to adjust certain behaviors of certain libraries.

I think you're confusing setting the environment before running a process, with setting the environment _within_ the process. If you're running a shell session, or even a compiled process which is just a "runner" for some other process - then certainly, we all do "export SOME_SETTING=value" and run things. But if you're writing a C library, which could well be used in a multi-threaded environment - you don't need to "adjust" anything, and should not invoke setenv. If your library is not pleased with the settings of another library, then it should start returning errors, or even exit() if you're a violent kind of a guy - but not setenv().

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#334

Earlier quoted context omitted.

What is wrong with main setting those things first and then starting your main program? That is what everyone else does.

Not “everyone” does that. You have individual files doing their own initialization when they get loaded. Including loading other files or modules. They might do it for testing purposes.

That does happen. Still there is a reason many avoid it. Probably every significant project has places where they do that. Still if it isn't in main it is always a little "magic" and that means hard to understand how the program works. (or worse randomly doesn't work because something is used before it is initialized)

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#335
post #329

Earlier quoted context omitted.

You also need the apps installed on whatever then, and enough CPU power to run those apps. I'm not saying you are wrong, but there is a lot of nuance here.

For CPU power, a Raspberry Pi today is faster than servers that ran whole medium to large businesses 20 years ago. Much of what people do with SaaS involves backend processes that could run on a 1990s era PC. There are exceptions, like large AI models and huge databases like web search, though in the case of AI models I can run pretty decent ones locally already, but on an admittedly expensive laptop. If the rate at…

Are you willing for doom quality? It got the job done, but compared to a modern 3d game it looks really bad.

Of course the elephant in the room remains: I need my data where I am.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#336
post #5

The 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 filed upstream in the meantime).

But that won't actually fix the underlying problem, namely that getenv and setenv (or unsetenv, probably) cannot safely be called from different threads. It seems like the only reliable way to fix this is to change these functions so that they exclusively acquire a mutex.

The mutex would have to be held by the caller until it no longer needs the string returned from the environment, or makes a copy:

   stdenvlock();    // imaginary function added to ISO C or POSIX
   char *home = getenv("HOME");
   char *home_copy = strdup(home);
   stdenvunlock();  // only here can we unlock
   // home pointer is now indeterminate
Other solutions:

1. Put the above sequence into a function, and don't expose the mutex. Thread-safe code must use:

   char *home = dupenv("HOME"); // imaginary function; caller responsible for freeing.

2. Provide environment lookup into a buffer:

   getenvbuf("HOME", mybuf, sizeof mybuf);  // returns some value that helps to resize the buffer

   
All functions that retain pointers out of the classic getenv remain unsafe.

A mutex can be provided to those applications that want to manipulate the environ array directly, or use getenv and setenv, or any combinations of these.

The main problem is all the code out there using getenv.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#337
This 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.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#338
post #96

Earlier quoted context omitted.

But that won't actually fix the underlying problem, namely that getenv and setenv (or unsetenv, probably) cannot safely be called from different threads. It seems like the only reliable way to fix this is to change these functions so that they exclusively acquire a mutex.

I have a different perspective: the underlying problem is calling setenv(). As far as I'm concerned, the environment is a read-only input parameter set on process creation like argv. It's not a mechanism for exchanging information within a process, as used here with SSL_CERT_FILE. And remember that the exec* family of calls has a version with an envp argument, which is what should be used if a child process is to be…

The underlying problem isn't just setenv, because the string returned by getenv can be invalidated by another call to getenv. ISO C says:

"The getenv function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but can be overwritten by a subsequent call to the getenv function."

In a single threaded virtual machine, you can immediately duplicate the string returned by getenv and stop using it, right there.

Under threads, getenv is not required to be safe.

I think that with some care, it may be; an environment implementation could guarantee that a non-mutating operation like getenv doesn't invalidate any previously returned strings.

I think POSIX does that. It allows getenv to reallocate the environ array, but not the strings themselves:

"Applications can change the entire environment in a single operation by assigning the environ variable to point to an array of character pointers to the new environment strings. After assigning a new value to environ, applications should not rely on the new environment strings remaining part of the environment, as a call to getenv(), secure_getenv(), [XSI] [Option Start] putenv(), [Option End] setenv(), unsetenv(), or any function that is dependent on an environment variable may, on noticing that environ has changed, copy the environment strings to a new array and assign environ to point to it."

environ is documented together with the exec family of functions; that's where this is found.

So whereas there are things not to like about environ, it can be the basis for thread safety of getenv in an application that doesn't mutate the environment.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#339

Earlier quoted context omitted.

But that won't actually fix the underlying problem, namely that getenv and setenv (or unsetenv, probably) cannot safely be called from different threads. It seems like the only reliable way to fix this is to change these functions so that they exclusively acquire a mutex.

No amount of locking can make the getenv API thread-safe, because it returns a pointer which gets invalidated by setenv, but lacks a way to release ownership over it and unblock setenv safely (or to free a returned copy). So setenv's existence makes getenv inherently unsafe unless you can ensure the entire application is at a safe point to use them.

According to ISO C, getenv returns a pointer to storage that can be overwritten by another call to getenv! Only POSIX slightly fixes it: the string comes from the environ array, and operations on environ by the library preserve the strings themselves (when not replacing or deleting them), just not the array. A program that calls nothing but getenv is okay on POSIX, not necessarily on ISO C.

Re: C stdlib isn't threadsafe and even safe Rust didn't save us

#340

Earlier quoted context omitted.

But that won't actually fix the underlying problem, namely that getenv and setenv (or unsetenv, probably) cannot safely be called from different threads. It seems like the only reliable way to fix this is to change these functions so that they exclusively acquire a mutex.

Please no. If your program wants to use the environment as an out-of-band global var for cross thread communication, you can make your own mutex.

That will break if any code which is not aware of the mutex calls getenv, even for a variable not related to the communication.
Post reply on HN