Live data from Hacker News

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

edgedb.com

51–60 of 370 posts

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

#51
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).

People get trained to ignore the ____UNSAFE_payattention__nevermindthatthisappears50timesinthisfile___ blocks and prefixes This also shows up in web frameworks where Vue has the v-html directive and react has dangerouslySetInnerHTML. Vue definitely has it better.

In the React world, the only times I've seen dangerouslySetInnerHTML consistently used is for outputting string literal CSS content (and this one is increasingly rare as build tools need less handholding), string literal JSON content (for JSON+LD), and string literal premade scripts (i.e. pixel tags from the marketing content). That's not to say there's no danger surface there, but it's not broadly used as a tool outside of code that's either really bad or really exhaustively hand-tuned.

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

#52
post #34

Earlier quoted context omitted.

The man page says: > POSIX.1 does not require setenv() or unsetenv() to be reentrant. A non-reentrant function cannot be thread safe. In general (for POSIX, libc and many other libraries: if the docs do not explicitly say "this function is thread safe" they are not).

> A non-reentrant function cannot be thread safe. Actually, a non-reentrant function can be thread-safe. A common example of such a function in libc being malloc().

I could be wrong but isnt that because each thread has its own heap?

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

#53
post #14

Earlier quoted context omitted.

It's time to move beyond this attitude and make things safe by default. For example, Solaris has a safer version of setenv(). "It is ridiculous that this has been a known problem for so long. It has wasted thousands of hours of people's time, either debugging the problems, or debating what to do about it. We know how to fix the problem." https://www.evanjones.ca/setenv-is-not-thread-safe.html

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 g…

How 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.

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

#54

It is weird that I got this right before Rust did. Because I use structured concurrency, I can make it so every thread has its own environment stack . To add to a new environment, I duplicate it, add the new variable, and push the new enviroment on the stack. Then I can use code blocks to delimit where that stack should be popped. [1] This is all perfectly safe, no `unsafe` required, and can even extend to other thin…

This isn't _really_ a Rust problem. Rust is a victim of POSIX.

If you have 1) C FFI interop in Yao, there's still a chance you might have two C libraries cause a crash without your code even being involved.

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

#55

Earlier quoted context omitted.

Env vars are good if you treat them as read-only within the process

Yeah, setenv should probably just not exist, and environment variables should be only set when spawning new processes.

The problem is that applications sometimes need to set environment variables which will be read by libraries in the same process. This is safe to do during startup, but at no later times.

Ideally all libraries which use environment variables should have APIs allowing you to override the env variables without calling setenv(), but that isn't always the case.

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

#56
post #24

I wonder why it is so hard for Rust to implement its own safe stdlib independent of C.

Linux is an unusual platform in that it allows you to call into it via assembly. Most other platforms require you to go through libc to do so. It's not really in Rust's hands.

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

#57
post #24

I wonder why it is so hard for Rust to implement its own safe stdlib independent of C.

It would be a tremendous amount of work, and would take years. Meanwhile, the problems are avoidable. It's not exactly the "rust way" to just remember and avoid problems, but everything in language design is compromises.

"Impossibru!!"

https://github.com/sunfishcode/eyra

Oh look:

> Why use Eyra? It fixes Rust's set_var unsoundness issue. The environment-variable implementation leaks memory internally (it is optional, but enabled by default), so setenv etc. are thread-safe.

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

#58
post #34

Earlier quoted context omitted.

> A non-reentrant function cannot be thread safe. Actually, a non-reentrant function can be thread-safe. A common example of such a function in libc being malloc().

By definition, a "reentrant function" is a function that may be invoked even when it has not returned yet from a previous invocation. So a non-reentrant function is a function that may not be invoked again between a previous invocation and returning from that invocation. When a function may be invoked from different threads, then it is certain that sometimes it will be invoked by a thread before returning from a prev…

Who has a signal safe malloc?

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

#59
post #14

Earlier quoted context omitted.

It's time to move beyond this attitude and make things safe by default. For example, Solaris has a safer version of setenv(). "It is ridiculous that this has been a known problem for so long. It has wasted thousands of hours of people's time, either debugging the problems, or debating what to do about it. We know how to fix the problem." https://www.evanjones.ca/setenv-is-not-thread-safe.html

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 g…

Of 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.

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

#60

Earlier quoted context omitted.

Yeah, setenv should probably just not exist, and environment variables should be only set when spawning new processes.

The problem is that applications sometimes need to set environment variables which will be read by libraries in the same process. This is safe to do during startup, but at no later times. Ideally all libraries which use environment variables should have APIs allowing you to override the env variables without calling setenv(), but that isn't always the case.

Yeah, the cows have certainly gotten out already.
Post reply on HN