This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Avoid lifetime UB in __thread_local_data()
ClosedPublic

Authored by vitalybuka on Mar 31 2022, 10:02 PM.

Details

Reviewers
ldionne
Group Reviewers
Restricted Project
Commits
rG0c99575df4a9: [libc++] Avoid lifetime UB in __thread_local_data()
Summary

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.

Diff Detail

Event Timeline

vitalybuka created this revision.Mar 31 2022, 10:02 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 31 2022, 10:02 PM
vitalybuka requested review of this revision.Mar 31 2022, 10:02 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 31 2022, 10:02 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript
vitalybuka edited the summary of this revision. (Show Details)Mar 31 2022, 10:03 PM
vitalybuka edited the summary of this revision. (Show Details)
vitalybuka edited the summary of this revision. (Show Details)
vitalybuka edited the summary of this revision. (Show Details)Apr 1 2022, 11:56 AM

Thanks for the patch! I'm not sure I understand how we'd catch this though, since D122869 doesn't touch any libc++ tests. Is there something we can do to fix our test setup in libc++ so as to catch this issue in our own tests?

libcxx/src/thread.cpp
118–125

I think we should add a comment like:

// 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.
vitalybuka marked an inline comment as done.Apr 11 2022, 12:12 PM

Thanks for the patch! I'm not sure I understand how we'd catch this though, since D122869 doesn't touch any libc++ tests. Is there something we can do to fix our test setup in libc++ so as to catch this issue in our own tests?

I am not sure as well. Do we have bot which runs libc++ tests with sanitizers?
My team runs some clang/llvm tests with msan and instrumented libc++, but no libc++ tests.

WDYT about:

__thread_specific_ptr::~__thread_specific_ptr() {
  destroyed = true;
}

__thread_specific_ptr::get() {
  _LIBCPP_ASSERT(!destroyed, ...
}

and then I can try to add test to simulated this behavior?

Thanks for the patch! I'm not sure I understand how we'd catch this though, since D122869 doesn't touch any libc++ tests. Is there something we can do to fix our test setup in libc++ so as to catch this issue in our own tests?

I am not sure as well. Do we have bot which runs libc++ tests with sanitizers?
My team runs some clang/llvm tests with msan and instrumented libc++, but no libc++ tests.

Yes, we run Asan, Tsan, Msan and UBsan on each patch in our pre-commit ci: https://buildkite.com/llvm-project/libcxx-ci/builds/10156. It looks like the patch as-is is failing under Msan, too.

To detect this, perhaps we're just missing -fsanitize-memory-use-after-dtor in our test setup? Is it enabled by default?

Thanks for the patch! I'm not sure I understand how we'd catch this though, since D122869 doesn't touch any libc++ tests. Is there something we can do to fix our test setup in libc++ so as to catch this issue in our own tests?

I am not sure as well. Do we have bot which runs libc++ tests with sanitizers?
My team runs some clang/llvm tests with msan and instrumented libc++, but no libc++ tests.

Yes, we run Asan, Tsan, Msan and UBsan on each patch in our pre-commit ci: https://buildkite.com/llvm-project/libcxx-ci/builds/10156. It looks like the patch as-is is failing under Msan, too.

To detect this, perhaps we're just missing -fsanitize-memory-use-after-dtor in our test setup? Is it enabled by default?

It's not, also it needs env MSAN_OPTIONS=poison_in_dtor=1
However those are good to be enabled by default, but it will break here and some other llvm places I need to fix.

vitalybuka edited the summary of this revision. (Show Details)Apr 18 2022, 9:13 PM

remove include

ldionne accepted this revision.Apr 25 2022, 1:51 PM
This revision is now accepted and ready to land.Apr 25 2022, 1:51 PM
This revision was automatically updated to reflect the committed changes.