Skip to content

fix(userspace/falco): fix watchdog race condition on timeout exchange#3820

Merged
poiana merged 1 commit intofalcosecurity:masterfrom
irozzo-1A:fix/watchdog-race-condition
Mar 12, 2026
Merged

fix(userspace/falco): fix watchdog race condition on timeout exchange#3820
poiana merged 1 commit intofalcosecurity:masterfrom
irozzo-1A:fix/watchdog-race-condition

Conversation

@irozzo-1A
Copy link
Contributor

@irozzo-1A irozzo-1A commented Mar 12, 2026

What type of PR is this?

Uncomment one (or more) /kind <> lines:

/kind bug

/kind cleanup

/kind design

/kind documentation

/kind failing-test

/kind feature

/kind release

Any specific area of the project related to this PR?

Uncomment one (or more) /area <> lines:

/area build

/area engine

/area tests

/area proposals

/area CI

What this PR does / why we need it:

The watchdog thread and stop() consume the timeout pointer with m_timeout.exchange(nullptr, ...). That exchange was using memory_order_release. The load part of the RMW needs acquire semantics so it synchronizes-with the release store in set_timeout()/cancel_timeout(); otherwise the consumer can see the pointer value without seeing the writes that initialized the timeout_data and payload (data race).
Use memory_order_acq_rel on the consumer exchanges so the load synchronizes-with the producer and the pointed-to memory is visible before use.

==================
WARNING: ThreadSanitizer: data race (pid=138643)
  Read of size 8 at 0xff70f48651f0 by thread T11:
    #0 watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::timeout_data::operator=(watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::timeout_data const&) /workspaces/falco/userspace/falco/watchdog.h:75:9 (falco+0x399764) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()::operator()() const /workspaces/falco/userspace/falco/watchdog.h:40:11 (falco+0x399764)
    #2 void std::__invoke_impl<void, watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>(std::__invoke_other, watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14 (falco+0x399614) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #3 std::__invoke_result<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>::type std::__invoke<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>(watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (falco+0x399614)
    #4 void std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (falco+0x399614)
    #5 std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>::operator()() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (falco+0x399614)
    #6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>>::_M_run() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (falco+0x399614)
    #7 <null> <null> (libstdc++.so.6+0xe1adc) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)

  Previous write of size 8 at 0xff70f48651f0 by thread T10:
    #0 operator new(unsigned long) <null> (falco+0x23c3a0) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::cancel_timeout() /workspaces/falco/userspace/falco/watchdog.h:70:29 (falco+0x392518) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #2 falco_outputs::worker() /workspaces/falco/userspace/falco/falco_outputs.cpp:318:6 (falco+0x392518)
    #3 void std::__invoke_impl<void, void (falco_outputs::*)() noexcept, falco_outputs*>(std::__invoke_memfun_deref, void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:74:14 (falco+0x3982ac) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #4 std::__invoke_result<void (falco_outputs::*)() noexcept, falco_outputs*>::type std::__invoke<void (falco_outputs::*)() noexcept, falco_outputs*>(void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (falco+0x3982ac)
    #5 void std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (falco+0x3982ac)
    #6 std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::operator()() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (falco+0x3982ac)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>>::_M_run() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (falco+0x3982ac)
    #8 <null> <null> (libstdc++.so.6+0xe1adc) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)

  As if synchronized via sleep:
    #0 nanosleep <null> (falco+0x1af658) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 void std::this_thread::sleep_for<long, std::ratio<1l, 1000l>>(std::chrono::duration<long, std::ratio<1l, 1000l>> const&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/this_thread_sleep.h:80:9 (falco+0x399948) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #2 watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()::operator()() const /workspaces/falco/userspace/falco/watchdog.h:48:5 (falco+0x399948)
    #3 void std::__invoke_impl<void, watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>(std::__invoke_other, watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14 (falco+0x399614) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #4 std::__invoke_result<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>::type std::__invoke<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>(watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (falco+0x399614)
    #5 void std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (falco+0x399614)
    #6 std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>::operator()() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (falco+0x399614)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::start(std::function<void (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)>, std::chrono::duration<long, std::ratio<1l, 1000l>>)::'lambda'()>>>::_M_run() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (falco+0x399614)
    #8 <null> <null> (libstdc++.so.6+0xe1adc) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)

  Location is heap block of size 40 at 0xff70f48651f0 allocated by thread T10:
    #0 operator new(unsigned long) <null> (falco+0x23c3a0) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::cancel_timeout() /workspaces/falco/userspace/falco/watchdog.h:70:29 (falco+0x392518) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #2 falco_outputs::worker() /workspaces/falco/userspace/falco/falco_outputs.cpp:318:6 (falco+0x392518)
    #3 void std::__invoke_impl<void, void (falco_outputs::*)() noexcept, falco_outputs*>(std::__invoke_memfun_deref, void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:74:14 (falco+0x3982ac) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #4 std::__invoke_result<void (falco_outputs::*)() noexcept, falco_outputs*>::type std::__invoke<void (falco_outputs::*)() noexcept, falco_outputs*>(void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (falco+0x3982ac)
    #5 void std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (falco+0x3982ac)
    #6 std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::operator()() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (falco+0x3982ac)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>>::_M_run() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (falco+0x3982ac)
    #8 <null> <null> (libstdc++.so.6+0xe1adc) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)

  Thread T11 (tid=138656, running) created by thread T10 at:
    #0 pthread_create <null> (falco+0x1b27b8) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xe1c64) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)
    #2 falco_outputs::worker() /workspaces/falco/userspace/falco/falco_outputs.cpp:295:5 (falco+0x3923ec) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #3 void std::__invoke_impl<void, void (falco_outputs::*)() noexcept, falco_outputs*>(std::__invoke_memfun_deref, void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:74:14 (falco+0x3982ac) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #4 std::__invoke_result<void (falco_outputs::*)() noexcept, falco_outputs*>::type std::__invoke<void (falco_outputs::*)() noexcept, falco_outputs*>(void (falco_outputs::*&&)() noexcept, falco_outputs*&&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:96:14 (falco+0x3982ac)
    #5 void std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:292:13 (falco+0x3982ac)
    #6 std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>::operator()() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:299:11 (falco+0x3982ac)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (falco_outputs::*)() noexcept, falco_outputs*>>>::_M_run() /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:244:13 (falco+0x3982ac)
    #8 <null> <null> (libstdc++.so.6+0xe1adc) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)

  Thread T10 (tid=138655, running) created by main thread at:
    #0 pthread_create <null> (falco+0x1b27b8) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xe1c64) (BuildId: 7c239d1743727054caa3df1ab866bfa72cd4dd93)
    #2 void std::_Construct<falco_outputs, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(falco_outputs*, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/stl_construct.h:119:25 (falco+0x2a6258) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #3 void std::allocator_traits<std::allocator<void>>::construct<falco_outputs, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(std::allocator<void>&, falco_outputs*, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/alloc_traits.h:661:4 (falco+0x2a5c18) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #4 std::_Sp_counted_ptr_inplace<falco_outputs, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(std::allocator<void>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/shared_ptr_base.h:604:4 (falco+0x2a5c18)
    #5 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<falco_outputs, std::allocator<void>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(falco_outputs*&, std::_Sp_alloc_shared_tag<std::allocator<void>>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/shared_ptr_base.h:972:6 (falco+0x2a5c18)
    #6 std::__shared_ptr<falco_outputs, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(std::_Sp_alloc_shared_tag<std::allocator<void>>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/shared_ptr_base.h:1712:14 (falco+0x2a5c18)
    #7 std::shared_ptr<falco_outputs>::shared_ptr<std::allocator<void>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(std::_Sp_alloc_shared_tag<std::allocator<void>>, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/shared_ptr.h:464:4 (falco+0x2a5c18)
    #8 std::shared_ptr<std::enable_if<!is_array<falco_outputs>::value, falco_outputs>::type> std::make_shared<falco_outputs, std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&>(std::shared_ptr<falco_engine>&, std::vector<falco::outputs::config, std::allocator<falco::outputs::config>>&, bool&, bool&, bool&, bool&, bool&, unsigned int&, bool&, unsigned long&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/shared_ptr.h:1009:14 (falco+0x2a5c18)
    #9 falco::app::actions::init_outputs(falco::app::state&) /workspaces/falco/userspace/falco/app/actions/init_outputs.cpp:60:14 (falco+0x2a5c18)
    #10 falco::app::run_result std::__invoke_impl<falco::app::run_result, falco::app::run_result (*&)(falco::app::state&), falco::app::state&>(std::__invoke_other, falco::app::run_result (*&)(falco::app::state&), falco::app::state&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:61:14 (falco+0x24494c) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #11 std::enable_if<is_invocable_r_v<falco::app::run_result, falco::app::run_result (*&)(falco::app::state&), falco::app::state&>, falco::app::run_result>::type std::__invoke_r<falco::app::run_result, falco::app::run_result (*&)(falco::app::state&), falco::app::state&>(falco::app::run_result (*&)(falco::app::state&), falco::app::state&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/invoke.h:114:9 (falco+0x24494c)
    #12 std::_Function_handler<falco::app::run_result (falco::app::state&), falco::app::run_result (*)(falco::app::state&)>::_M_invoke(std::_Any_data const&, falco::app::state&) /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:290:9 (falco+0x24494c)
    #13 std::function<falco::app::run_result (falco::app::state&)>::operator()(falco::app::state&) const /usr/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9 (falco+0x23ee18) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #14 falco::app::run(falco::app::state&, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /workspaces/falco/userspace/falco/app/app.cpp:95:44 (falco+0x23ee18)
    #15 falco::app::run(int, char**, bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /workspaces/falco/userspace/falco/app/app.cpp:48:9 (falco+0x23e604) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #16 falco_run(int, char**, bool&) /workspaces/falco/userspace/falco/falco.cpp:44:7 (falco+0x23d520) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)
    #17 main /workspaces/falco/userspace/falco/falco.cpp:67:14 (falco+0x23d810) (BuildId: abf30f4ef9ddb047e7bb5604c3628bf69cfba945)

SUMMARY: ThreadSanitizer: data race /workspaces/falco/userspace/falco/watchdog.h:75:9 in watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::timeout_data::operator=(watchdog<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>::timeout_data const&)
==================

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

fix(userspace/falco): fix race condition in watchdog

The watchdog thread and stop() consume the timeout pointer with
m_timeout.exchange(nullptr, ...). That exchange was using
memory_order_release. The load part of the RMW needs acquire
semantics so it synchronizes-with the release store in
set_timeout()/cancel_timeout(); otherwise the consumer can see
the pointer value without seeing the writes that initialized
the timeout_data and payload (data race).
Use memory_order_acq_rel on the consumer exchanges so the load
synchronizes-with the producer and the pointed-to memory is
visible before use.

Signed-off-by: irozzo-1A <[email protected]>
Copy link
Member

@leogr leogr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good catch. The bug has been here since 2020.

The fix LGTM!

@poiana
Copy link
Contributor

poiana commented Mar 12, 2026

LGTM label has been added.

DetailsGit tree hash: 93cab89060b1b4a4fc5f778a3895cb0d70ef92a7

@leogr
Copy link
Member

leogr commented Mar 12, 2026

/milestone 0.44.0

@poiana poiana added this to the 0.44.0 milestone Mar 12, 2026
@github-project-automation github-project-automation bot moved this from Todo to In progress in Falco Roadmap Mar 12, 2026
@poiana
Copy link
Contributor

poiana commented Mar 12, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: irozzo-1A, leogr, sgaist

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@poiana poiana merged commit 8989870 into falcosecurity:master Mar 12, 2026
55 of 56 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in Falco Roadmap Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants