<html>
    <head>
      <base href="https://llvm.org/bugs/" />
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW --- - libcxx: racy use-after-free on condition_variable"
   href="https://llvm.org/bugs/show_bug.cgi?id=23293">23293</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>libcxx: racy use-after-free on condition_variable
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libc++
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>release blocker
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>All Bugs
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedclangbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>dvyukov@google.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvmbugs@cs.uiuc.edu, mclow.lists@gmail.com
          </td>
        </tr>

        <tr>
          <th>Classification</th>
          <td>Unclassified
          </td>
        </tr></table>
      <p>
        <div>
        <pre>clang version 3.7.0 (trunk 235293)
Target: x86_64-unknown-linux-gnu

The program is:

#include <iostream>
#include <memory>
#include <future>
#include <atomic>

std::atomic<int> _counter;

int main()
{
    _counter = 1;

    auto f = std::async(std::launch::async,
                        []{
                            _counter += 2;
                            return true;
                        });
    std::cout << "Result: " << std::boolalpha << f.get() << "\n";
    return 0;
}

ThreadSanitizer says:

==================
WARNING: ThreadSanitizer: data race (pid=19479)
  Write of size 8 at 0x7d200000bfc0 by main thread:
    #0 pthread_cond_destroy
/ssd/src/llvm/build/../projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:1153
(a.out+0x00000044aa8c)
    #1 std::__1::condition_variable::~condition_variable()
build/../projects/libcxx/src/condition_variable.cpp:23:5
(libc++.so.1+0x00000008cb69)
    #2 std::__1::__assoc_sub_state::~__assoc_sub_state()
build/bin/../include/c++/v1/future:515:24 (a.out+0x00000049999a)
    #3 std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0>
<span class="quote">>::~__async_assoc_state() build/bin/../include/c++/v1/future:940:7</span >
(a.out+0x0000004992d5)
    #4 std::__1::__assoc_state<bool>::__on_zero_shared()
build/bin/../include/c++/v1/future:635:5 (a.out+0x000000499a63)
    #5 std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0>
<span class="quote">>::__on_zero_shared() build/bin/../include/c++/v1/future:990:5</span >
(a.out+0x00000049930d)
    #6 std::__1::__shared_count::__release_shared()
build/../projects/libcxx/src/memory.cpp:63:9 (libc++.so.1+0x00000008eb20)
    #7 std::__1::__release_shared_count::operator()(std::__1::__shared_count*)
build/bin/../include/c++/v1/future:1155:41 (a.out+0x00000049a155)
    #8 std::__1::unique_ptr<std::__1::__shared_count,
std::__1::__release_shared_count>::reset(std::__1::__shared_count*)
build/bin/../include/c++/v1/memory:2669:13 (a.out+0x0000004996bd)
    #9 std::__1::unique_ptr<std::__1::__shared_count,
std::__1::__release_shared_count>::~unique_ptr()
build/bin/../include/c++/v1/memory:2637 (a.out+0x0000004996bd)
    #10 std::__1::future<bool>::get() build/bin/../include/c++/v1/future:1173
(a.out+0x0000004996bd)
    #11 main /tmp/shared_ptr.cc:17:50 (a.out+0x000000498f62)

  Previous read of size 8 at 0x7d200000bfc0 by thread T1:
    #0 pthread_cond_broadcast
/ssd/src/llvm/build/../projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:1146
(a.out+0x000000447e40)
    #1 std::__1::condition_variable::notify_all()
build/../projects/libcxx/src/condition_variable.cpp:35:5
(libc++.so.1+0x00000008cbc9)
    #2 void std::__1::__assoc_state<bool>::set_value<bool>(bool&&)
build/bin/../include/c++/v1/future:655:5 (a.out+0x000000499c01)
    #3 std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0>
<span class="quote">>::__execute() build/bin/../include/c++/v1/future:975:9 (a.out+0x00000049934e)</span >
    #4
_ZNSt3__18__invokeIMNS_19__async_assoc_stateIbNS_12__async_funcIZ4mainE3$_0JEEEEEFvvEPS5_JEvEEDTcldsdeclsr3std3__1E7forwardIT0_Efp0_Efp_spclsr3std3__1E7forwardIT1_Efp1_EEEOT_OS9_DpOSA_
build/bin/../include/c++/v1/__functional_base:382:12 (a.out+0x0000004994a1)
    #5 void std::__1::__thread_execute<void
(std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0> >::*)(),
std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0> >*,
1ul>(std::__1::tuple<void (std::__1::__async_assoc_state<bool,
std::__1::__async_func<main::$_0> >::*)(), std::__1::__async_assoc_state<bool,
std::__1::__async_func<main::$_0> >*>&, std::__1::__tuple_indices<1ul>)
build/bin/../include/c++/v1/thread:337 (a.out+0x0000004994a1)
    #6 void* std::__1::__thread_proxy<std::__1::tuple<void
(std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0> >::*)(),
std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0> >*>
<span class="quote">>(void*) build/bin/../include/c++/v1/thread:347 (a.out+0x0000004994a1)</span >

  Location is heap block of size 120 at 0x7d200000bf80 allocated by main
thread:
    #0 operator new(unsigned long)
/ssd/src/llvm/build/../projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:615
(a.out+0x00000043e08f)
    #1 std::__1::future<bool> std::__1::__make_async_assoc_state<bool,
std::__1::__async_func<main::$_0> >(std::__1::__async_func<main::$_0>&&)
build/bin/../include/c++/v1/future:2312:13 (a.out+0x000000499035)
    #2
std::__1::future<std::__1::__invoke_of<std::__1::decay<main::$_0>::type>::type>
std::__1::async<main::$_0>(std::__1::launch, main::$_0&&)
build/bin/../include/c++/v1/future:2361:16 (a.out+0x000000498fce)
    #3 main /tmp/shared_ptr.cc:12:14 (a.out+0x000000498f0d)

  Thread T1 (tid=19481, finished) created by main thread at:
    #0 pthread_create
/ssd/src/llvm/build/../projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:951
(a.out+0x000000447123)
    #1 std::__1::thread::thread<void (std::__1::__async_assoc_state<bool,
std::__1::__async_func<main::$_0> >::*)(), std::__1::__async_assoc_state<bool,
std::__1::__async_func<main::$_0> >*, void>(void
(std::__1::__async_assoc_state<bool, std::__1::__async_func<main::$_0>
<span class="quote">>::*&&)(), std::__1::__async_assoc_state<bool,</span >
std::__1::__async_func<main::$_0> >*&&)
build/bin/../include/c++/v1/thread:359:16 (a.out+0x000000499282)
    #2 std::__1::future<bool> std::__1::__make_async_assoc_state<bool,
std::__1::__async_func<main::$_0> >(std::__1::__async_func<main::$_0>&&)
build/bin/../include/c++/v1/future:2313:5 (a.out+0x0000004990a4)
    #3
std::__1::future<std::__1::__invoke_of<std::__1::decay<main::$_0>::type>::type>
std::__1::async<main::$_0>(std::__1::launch, main::$_0&&)
build/bin/../include/c++/v1/future:2361:16 (a.out+0x000000498fce)
    #4 main /tmp/shared_ptr.cc:12:14 (a.out+0x000000498f0d)

SUMMARY: ThreadSanitizer: data race
build/../projects/libcxx/src/condition_variable.cpp:23:5 in
std::__1::condition_variable::~condition_variable()
==================

The program was built as:

clang++ test.cc -fsanitize=thread -std=c++14 -stdlib=libc++ -lc++abi -Wall -g
-O1

with tsan-instrumented libcxx (see instructions here
<a href="https://code.google.com/p/memory-sanitizer/wiki/LibcxxHowTo">https://code.google.com/p/memory-sanitizer/wiki/LibcxxHowTo</a>).

The problem is here:

__assoc_state<_Rp>::set_value(_Arg&& __arg)
{
    unique_lock<mutex> __lk(this->__mut_);
#ifndef _LIBCPP_NO_EXCEPTIONS
    if (this->__has_value())
        throw
future_error(make_error_code(future_errc::promise_already_satisfied));
#endif
    ::new(&__value_) _Rp(_VSTD::forward<_Arg>(__arg));
    this->__state_ |= base::__constructed | base::ready;
    __lk.unlock();
    __cv_.notify_all();
}

The function first unlocks the mutex and after than signals the cond var. But
once the mutex is unlocked the object can be destroyed as it is in ready state.</pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>