On unix targets, the Rust standard library's exit function forwards to libc's exit, but there is no warning about the C UB in the documentation. Wouldn't it be helpful for the Rust documentation to include a note about this?
My guess is is, there is no "UB in the Rust sense" because
there are no atexit functions except those that are registered with unsafe. In this case, the atexit function should have this warning
of course, two threads could race towards exit, one calling exit(0), the other calling exit(1). It is not surprising to me that C++ calls this UB because the exit code is part of the abstract machine. I think that this is not the case for Rust and this is a perfectly fine Race condition.
exit is thread-safe. Installing atexit handlers which free resources other threads might still be using is not thread-safe and is a bug in whatever code is installing those atexit handlers, and should be fixed there.
But it seems libc::exit is not thread safe, as defined by the C standard. If Rust wants to call libc, it must adhere to libc's contract. It's the C standard that is in charge of defining what is and isn't UB when calling C APIs. Indeed, per the comment that proposed to close the issue
We feel that if a library has atexit handlers that aren't thread-safe, that's an issue in that library. We can't do anything to fix that; that requires appropriate locking in the library. We shouldn't call quick_exit, because such a race could just as easily happen in an at_quick_exit handler. And we shouldn't call _Exit or _exit and bypass the atexit handlers, because people may legitimately be relying on those atexit handlers, and there's no issue if your atexit handlers are thread-safe.
We do understand that exit is not marked as MT-safe; however, our understanding is that in practice every major C library seems to use an appropriate lock around the atexit array. If there's a specific C library that doesn't, we'd be willing to consider a workaround on that platform (e.g. wrapping it with our own lock), but otherwise we'd propose to close this.
"Is not marked as MT-safe" means "it's not MT-safe", or, it's UB to call concurrently from other threads, without a lock. The current Rust behavior is like, knowingly doing something that is UB, but in the opinion of Rust developers shouldn't be.
The only solution I see here is to simply not call libc, at all.
It's not UB, it's unsound. UB is a property of a specific execution, which can rely on implementation details. Your quote says it is known that all currently used implementations are actually MT-safe, it's just technically std is violating a safety-precondition of the generic libc API which could lead to UB on some other implementations.
Ok. So this means that Rust currently has an unwritten rule that before adding a new target with a new libc, Rust developers must make sure that the libc does the expected thing here (even though the expected thing isn't required by any C standard)
Is it even "unsound" in the Rust sense? No safe Rust can cause this UB.
It probably is a practical issue regarding interop: in C/C++ it is UB to race to exit and if a program does not do that, nonthreadsafe atexit-handlers are fine, but are not fine in safe Rust... Unless I got something backwards.