exit(): add a lock.

POSIX hasn't accepted https://austingroupbugs.net/view.php?id=1845 yet,
but all the libc maintainers who've commented on
https://www.openwall.com/lists/libc-coord/2024/07/24/4 agree that this
is a good idea, with the only disagreement being whether or not the
mutex should be recursive.

I preferred the recursive mutex because suddenly disallowing exit() from
an atexit handler is an unnecessary behavior change that seemed likely
to trip someone up, and glibc found that they have existing examples of
exit() called from ELF destructors or atexit() handlers in test cases if
nothing else (https://sourceware.org/bugzilla/show_bug.cgi?id=31997).

So go with the recursive mutex option. The new test fails most of the
time without this fix, and runs fine with this fix.

Change-Id: I33c2229512e70113739e0dd9ffd2a745292ba8c3
diff --git a/libc/bionic/exit.cpp b/libc/bionic/exit.cpp
index a5aed78..8eda5b2 100644
--- a/libc/bionic/exit.cpp
+++ b/libc/bionic/exit.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <pthread.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -34,8 +35,13 @@
 extern "C" void __cxa_finalize(void* dso_handle);
 extern "C" void __cxa_thread_finalize();
 
+static pthread_mutex_t g_exit_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void exit(int status) {
+  // https://austingroupbugs.net/view.php?id=1845
+  pthread_mutex_lock(&g_exit_mutex);
+
   __cxa_thread_finalize();
   __cxa_finalize(nullptr);
   _exit(status);