public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug sanitizer/113244] New: Potential thread sanitizer false positive with future exception
@ 2024-01-05 17:06 gcc-bugzilla at mhxnet dot de
  2024-01-05 21:54 ` [Bug sanitizer/113244] " pinskia at gcc dot gnu.org
  2024-01-06 18:05 ` gcc-bugzilla at mhxnet dot de
  0 siblings, 2 replies; 3+ messages in thread
From: gcc-bugzilla at mhxnet dot de @ 2024-01-05 17:06 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113244

            Bug ID: 113244
           Summary: Potential thread sanitizer false positive with future
                    exception
           Product: gcc
           Version: 13.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gcc-bugzilla at mhxnet dot de
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org, marxin at gcc dot gnu.org
  Target Milestone: ---

Created attachment 56994
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=56994&action=edit
C++ code to reproduce the issue

Using `g++-13 (Gentoo 13.2.1_p20230826 p7) 13.2.1 20230826` and compiling the
attached program with

    g++-13 -std=c++20 -g -O2 -fsanitize=thread -fno-omit-frame-pointer -o
packaged_task packaged_task.cpp

Thread Sanitizer will likely, but not always, report several data races related
to the exception stored in the future. The races are between reading from the
exception object in the catch block on the main thread and destroying the
exception as a result of packaged_task going out of scope in one of the worker
threads.

    ==================
    WARNING: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)
(pid=7610)
      Write of size 8 at 0x7b2c00031460 by thread T9:
        #0 ~error_base /home/mhx/src/c++/test/packaged_task.cpp:12
(packaged_task+0x5574)
        #1 ~my_error /home/mhx/src/c++/test/packaged_task.cpp:22
(packaged_task+0x5574)
        #2 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:105
(libstdc++.so.6+0xb2f30)
        #3 std::__future_base::_Result<void>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:672
(packaged_task+0x5287)
        #4
std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*)
const /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:229
(packaged_task+0x5287)
        #5 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::~unique_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/unique_ptr.h:404
(packaged_task+0x5287)
        #6 std::__future_base::_State_baseV2::~_State_baseV2()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:344
(packaged_task+0x5287)
        #7 std::__future_base::_Task_state_base<void ()>::~_Task_state_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1450
(packaged_task+0x5287)
        #8 ~_Task_state
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1477
(packaged_task+0x5287)
        #9 destroy_at<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_construct.h:88
(packaged_task+0x5287)
        #10 destroy<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:559
(packaged_task+0x5287)
        #11 _M_dispose
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:613
(packaged_task+0x5287)
        #12
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:175
(packaged_task+0x83bc)
        #13 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:361
(packaged_task+0x83bc)
        #14 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1071
(packaged_task+0x83bc)
        #15 std::__shared_ptr<std::__future_base::_Task_state_base<void ()>,
(__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1524
(packaged_task+0x83bc)
        #16 std::shared_ptr<std::__future_base::_Task_state_base<void ()>
>::~shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr.h:175
(packaged_task+0x83bc)
        #17 std::packaged_task<void ()>::~packaged_task()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1594
(packaged_task+0x83bc)
        #18 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:287
(packaged_task+0x645c)
        #19 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_reset() /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:318
(packaged_task+0x645c)
        #20 std::_Optional_payload<std::packaged_task<void ()>, false, false,
false>::~_Optional_payload()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:439
(packaged_task+0x645c)
        #21 std::_Optional_base<std::packaged_task<void ()>, false,
false>::~_Optional_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:510
(packaged_task+0x645c)
        #22 std::optional<std::packaged_task<void ()> >::~optional()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:705
(packaged_task+0x645c)
        #23 worker /home/mhx/src/c++/test/packaged_task.cpp:45
(packaged_task+0x645c)
        #24 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #25 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #26 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #27 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #28 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #29 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Previous read of size 8 at 0x7b2c00031460 by main thread:
        #0 main /home/mhx/src/c++/test/packaged_task.cpp:96
(packaged_task+0x3c4a)

      Location is heap block of size 168 at 0x7b2c000313e0 allocated by thread
T9:
        #0 malloc
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:681
(libtsan.so.2+0x3a963)
        #1 __cxa_allocate_exception
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_alloc.cc:398
(libstdc++.so.6+0xb1fbf)
        #2 std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>::operator()() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_function.h:591
(packaged_task+0x6bbb)
        #3
std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:589
(packaged_task+0x6bbb)
        #4 void std::__invoke_impl<void, void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*,
bool*>(std::__invoke_memfun_deref, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:74
(packaged_task+0x697a)
        #5 std::__invoke_result<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>::type
std::__invoke<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x697a)
        #6 std::call_once<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&,
bool*&&)::{lambda()#1}::operator()() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:900
(packaged_task+0x697a)
        #7
std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&,
bool*&&)::{lambda()#1}>(void
(std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*,
bool*))::{lambda()#1}::operator()() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:836
(packaged_task+0x697a)
        #8
std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&,
bool*&&)::{lambda()#1}>(void
(std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::_FUN()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:836
(packaged_task+0x697a)
        #9 pthread_once
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1551
(libtsan.so.2+0x5d742)
        #10 __gthread_once
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/x86_64-pc-linux-gnu/bits/gthr-default.h:700
(packaged_task+0x6f4e)
        #11 void std::call_once<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:907
(packaged_task+0x6f4e)
        #12
std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>, bool)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:428
(packaged_task+0x62d0)
        #13 _M_run
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1494
(packaged_task+0x62d0)
        #14 std::packaged_task<void ()>::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1628
(packaged_task+0x62d0)
        #15 worker /home/mhx/src/c++/test/packaged_task.cpp:44
(packaged_task+0x62d0)
        #16 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #17 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #18 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #19 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #20 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #21 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Thread T9 (tid=7620, running) created by main thread at:
        #0 pthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036
(libtsan.so.2+0x3b7c5)
        #1 __gthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663
(libstdc++.so.6+0xe1644)
        #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State,
std::default_delete<std::thread::_State> >, void (*)())
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:172
(libstdc++.so.6+0xe1644)
        #3 std::thread& std::vector<std::thread, std::allocator<std::thread>
>::emplace_back<void (&)()>(void (&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:123
(packaged_task+0x4044)
        #4 start_workers /home/mhx/src/c++/test/packaged_task.cpp:58
(packaged_task+0x4044)
        #5 main /home/mhx/src/c++/test/packaged_task.cpp:74
(packaged_task+0x4044)

    SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)
/home/mhx/src/c++/test/packaged_task.cpp:12 in ~error_base
    ==================
    ==================
    WARNING: ThreadSanitizer: data race (pid=7610)
      Write of size 8 at 0x7b2c00031468 by thread T9:
        #0 free
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:740
(libtsan.so.2+0x3ae4b)
        #1 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:107
(libstdc++.so.6+0xb2f38)
        #2 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:96
(libstdc++.so.6+0xb2f38)
        #3 std::__future_base::_Result<void>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:672
(packaged_task+0x5287)
        #4
std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*)
const /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:229
(packaged_task+0x5287)
        #5 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::~unique_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/unique_ptr.h:404
(packaged_task+0x5287)
        #6 std::__future_base::_State_baseV2::~_State_baseV2()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:344
(packaged_task+0x5287)
        #7 std::__future_base::_Task_state_base<void ()>::~_Task_state_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1450
(packaged_task+0x5287)
        #8 ~_Task_state
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1477
(packaged_task+0x5287)
        #9 destroy_at<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_construct.h:88
(packaged_task+0x5287)
        #10 destroy<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:559
(packaged_task+0x5287)
        #11 _M_dispose
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:613
(packaged_task+0x5287)
        #12
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:175
(packaged_task+0x83bc)
        #13 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:361
(packaged_task+0x83bc)
        #14 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1071
(packaged_task+0x83bc)
        #15 std::__shared_ptr<std::__future_base::_Task_state_base<void ()>,
(__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1524
(packaged_task+0x83bc)
        #16 std::shared_ptr<std::__future_base::_Task_state_base<void ()>
>::~shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr.h:175
(packaged_task+0x83bc)
        #17 std::packaged_task<void ()>::~packaged_task()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1594
(packaged_task+0x83bc)
        #18 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:287
(packaged_task+0x645c)
        #19 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_reset() /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:318
(packaged_task+0x645c)
        #20 std::_Optional_payload<std::packaged_task<void ()>, false, false,
false>::~_Optional_payload()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:439
(packaged_task+0x645c)
        #21 std::_Optional_base<std::packaged_task<void ()>, false,
false>::~_Optional_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:510
(packaged_task+0x645c)
        #22 std::optional<std::packaged_task<void ()> >::~optional()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:705
(packaged_task+0x645c)
        #23 worker /home/mhx/src/c++/test/packaged_task.cpp:45
(packaged_task+0x645c)
        #24 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #25 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #26 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #27 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #28 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #29 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Previous read of size 8 at 0x7b2c00031468 by main thread:
        #0 std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_M_data() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/basic_string.h:223
(packaged_task+0x4f7d)
        #1 std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::c_str() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/basic_string.h:2584
(packaged_task+0x4f7d)
        #2 what /home/mhx/src/c++/test/packaged_task.cpp:16
(packaged_task+0x4f7d)
        #3 main /home/mhx/src/c++/test/packaged_task.cpp:96
(packaged_task+0x3c5e)

      Thread T9 (tid=7620, running) created by main thread at:
        #0 pthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036
(libtsan.so.2+0x3b7c5)
        #1 __gthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663
(libstdc++.so.6+0xe1644)
        #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State,
std::default_delete<std::thread::_State> >, void (*)())
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:172
(libstdc++.so.6+0xe1644)
        #3 std::thread& std::vector<std::thread, std::allocator<std::thread>
>::emplace_back<void (&)()>(void (&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:123
(packaged_task+0x4044)
        #4 start_workers /home/mhx/src/c++/test/packaged_task.cpp:58
(packaged_task+0x4044)
        #5 main /home/mhx/src/c++/test/packaged_task.cpp:74
(packaged_task+0x4044)

    SUMMARY: ThreadSanitizer: data race
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:107
in std::__exception_ptr::exception_ptr::_M_release()
    ==================
    errors: 20000
    ThreadSanitizer: reported 2 warnings

I believe what happens is that future::get() is called on the main thread,
rethrowing the stored exception, before the packaged_task in the worker thread
goes out of scope. Hence the exception_ptr refcount is 1 by the time
packaged_task goes out of scope and the exception object is freed on the worker
thread.

I *think* the code and the behaviour are actually fine and the reported race is
a false positive.

This actually reproduces quite reliably on godbolt with both gcc-10 and gcc-13:
https://gcc.godbolt.org/z/E97exx4MP

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [Bug sanitizer/113244] Potential thread sanitizer false positive with future exception
  2024-01-05 17:06 [Bug sanitizer/113244] New: Potential thread sanitizer false positive with future exception gcc-bugzilla at mhxnet dot de
@ 2024-01-05 21:54 ` pinskia at gcc dot gnu.org
  2024-01-06 18:05 ` gcc-bugzilla at mhxnet dot de
  1 sibling, 0 replies; 3+ messages in thread
From: pinskia at gcc dot gnu.org @ 2024-01-05 21:54 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113244

--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
I suspect this is because libstdc++.so is NOT instrumented for TSAN.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [Bug sanitizer/113244] Potential thread sanitizer false positive with future exception
  2024-01-05 17:06 [Bug sanitizer/113244] New: Potential thread sanitizer false positive with future exception gcc-bugzilla at mhxnet dot de
  2024-01-05 21:54 ` [Bug sanitizer/113244] " pinskia at gcc dot gnu.org
@ 2024-01-06 18:05 ` gcc-bugzilla at mhxnet dot de
  1 sibling, 0 replies; 3+ messages in thread
From: gcc-bugzilla at mhxnet dot de @ 2024-01-06 18:05 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113244

--- Comment #2 from Marcus Holland-Moritz <gcc-bugzilla at mhxnet dot de> ---
(In reply to Andrew Pinski from comment #1)
> I suspect this is because libstdc++.so is NOT instrumented for TSAN.

This is certainly possible.

Is there documentation available on how to build a suitably instrumented
libstdc++.so?

I've spent a few hours now trying to build

- all of gcc-13.2/gcc-12.3
- only libstdc++-v3 from gcc-13.2/gcc-12.3

with the following flags:

  CFLAGS="-fsanitize=thread -g -O2 -fno-omit-frame-pointer"
  CXXFLAGS="-fsanitize=thread -g -O2 -fno-omit-frame-pointer"
  LDFLAGS="-fsanitize=thread"

All of my attempts failed due to strange errors (which I can list here more
systematically if desired). A "plain" gcc build with no sanitizer-specific
compiler/linker flags works just fine.

Given that "libstdc++.so is NOT instrumented for TSAN" is a frequent response
to similar false positive reports, I'm somewhat surprised by the lack of easily
discoverable information on how to build an instrumented library. It also makes
me wonder how useful the thread sanitizer (maybe other sanitizers as well) is
at all without such an instrumented library. If an instrumented library is a
requirement for `-fsanitize=thread` to work properly, there should probably
exist a `configure` option to automatically build the required instrumented
library versions (and ideally the right version would be used when linking with
`-fsanitize=thread`).

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2024-01-06 18:05 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-05 17:06 [Bug sanitizer/113244] New: Potential thread sanitizer false positive with future exception gcc-bugzilla at mhxnet dot de
2024-01-05 21:54 ` [Bug sanitizer/113244] " pinskia at gcc dot gnu.org
2024-01-06 18:05 ` gcc-bugzilla at mhxnet dot de

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).