[libcxx-commits] [libcxx] 0c99575 - [libc++] Avoid lifetime UB in __thread_local_data()

Vitaly Buka via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 25 14:56:54 PDT 2022


Author: Vitaly Buka
Date: 2022-04-25T14:56:44-07:00
New Revision: 0c99575df4a9395e32bf1121c103616ba812accb

URL: https://github.com/llvm/llvm-project/commit/0c99575df4a9395e32bf1121c103616ba812accb
DIFF: https://github.com/llvm/llvm-project/commit/0c99575df4a9395e32bf1121c103616ba812accb.diff

LOG: [libc++] Avoid lifetime UB in __thread_local_data()

Detected on many lld tests with -fsanitize-memory-use-after-dtor.
Also https://lab.llvm.org/buildbot/#/builders/sanitizer-x86_64-linux-fast after D122869 will report a lot of them.

Threads may outlive static variables. Even if ~__thread_specific_ptr() does nothing, lifetime of members ends with ~ and accessing the value is UB https://eel.is/c++draft/basic.life#1

```
==9214==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x557e1cec4539 in __libcpp_tls_set ../include/c++/v1/__threading_support:428:12
    #1 0x557e1cec4539 in set_pointer ../include/c++/v1/thread:196:5
    #2 0x557e1cec4539 in void* std::__msan::__thread_proxy<
      std::__msan::tuple<...>, llvm::parallel::detail::(anonymous namespace)::ThreadPoolExecutor::ThreadPoolExecutor(llvm::ThreadPoolStrategy)::'lambda'()::operator()() const::'lambda'()> >(void*) ../include/c++/v1/thread:285:27

  Memory was marked as uninitialized
    #0 0x557e10a0759d in __sanitizer_dtor_callback compiler-rt/lib/msan/msan_interceptors.cpp:940:5
    #1 0x557e1d8c478d in std::__msan::__thread_specific_ptr<std::__msan::__thread_struct>::~__thread_specific_ptr() libcxx/include/thread:188:1
    #2 0x557e10a07dc0 in MSanCxaAtExitWrapper(void*) compiler-rt/lib/msan/msan_interceptors.cpp:1151:3
```

The test needs D123979 or  -fsanitize-memory-param-retval enabled by default.

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D122864

Added: 
    libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp

Modified: 
    libcxx/src/thread.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp
index 0734659d2ddf2..ce2822dbac85e 100644
--- a/libcxx/src/thread.cpp
+++ b/libcxx/src/thread.cpp
@@ -115,8 +115,13 @@ sleep_for(const chrono::nanoseconds& ns)
 __thread_specific_ptr<__thread_struct>&
 __thread_local_data()
 {
-    static __thread_specific_ptr<__thread_struct> __p;
-    return __p;
+  // Even though __thread_specific_ptr's destructor doesn't actually destroy
+  // anything (see comments there), we can't call it at all because threads may
+  // outlive the static variable and calling its destructor means accessing an
+  // object outside of its lifetime, which is UB.
+  alignas(__thread_specific_ptr<__thread_struct>) static char __b[sizeof(__thread_specific_ptr<__thread_struct>)];
+  static __thread_specific_ptr<__thread_struct>* __p = new (__b) __thread_specific_ptr<__thread_struct>();
+  return *__p;
 }
 
 // __thread_struct_imp

diff  --git a/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp
new file mode 100644
index 0000000000000..8c1b6ebade54d
--- /dev/null
+++ b/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++03
+
+#include "make_test_thread.h"
+
+void func() {}
+
+struct T {
+  ~T() {
+    // __thread_local_data is expected to be destroyed as it was created
+    // from the main(). Now trigger another access.
+    support::make_test_thread(func).join();
+  }
+} t;
+
+int main(int, char**) {
+  // Triggers construction of __thread_local_data.
+  support::make_test_thread(func).join();
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list