Skip to content

[BUG] Data races #329

Open
Open
@FlorianChevassu

Description

@FlorianChevassu

Prerequisites

Description

The documentation states that All functions are guaranteed to be completely reentrant and thread-safe (unless differently specified). However, the webserver implementation does not use any protection while reading/modifying its member variables (registered_resources, registered_resources_str, bans and allowances).

Steps to Reproduce

Here is a small test that show the issue when compiled using clang-16 with thread sanitizing enabled:

LT_BEGIN_AUTO_TEST(basic_suite, thread_safety)
    simple_resource resource;

    std::atomic_bool done = false;
    auto register_thread = std::thread([&]() {
        int i = 0;
        using namespace std::chrono;
        while (!done) {
            ws->register_resource(
                    std::string("/route") + std::to_string(++i), &resource);
        }
    });

    auto get_thread = std::thread([&](){
        while (!done) {
            CURL *curl = curl_easy_init();
            std::string s;
            std::string url = "localhost:" PORT_STRING "/route" + std::to_string(
                                            (int)((rand() * 10000000.0) / RAND_MAX));
            curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
            curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
            curl_easy_perform(curl);
            curl_easy_cleanup(curl);
        }
    });

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(10s);
    done = true;
    if (register_thread.joinable()) {
        register_thread.join();
    }
    if (get_thread.joinable()) {
        get_thread.join();
    }
    LT_CHECK_EQ(1, 1);
LT_END_AUTO_TEST(thread_safety)

Expected behavior:

No data races are detected by clang.

Actual behavior:
Clang reports the following data race (and others):

Running test (1): thread_safety
==================
WARNING: ThreadSanitizer: data race (pid=21795)
  Read of size 8 at 0x7b1400003550 by thread T1:
    #0 memcmp /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/../../sanitizer_common/sanitizer_common_interceptors.inc:939:3 (lt-basic+0x67439)
    #1 memcmp /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/../../sanitizer_common/sanitizer_common_interceptors.inc:935:1 (lt-basic+0x67439)
    #2 httpserver::webserver::finalize_answer(MHD_Connection*, httpserver::details::modded_request*, char const*) <null> (libhttpserver.so.0+0x227b2)
    #3 MHD_connection_handle_idle <null> (libmicrohttpd.so.12+0xd1ab) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #4 call_handlers daemon.c (libmicrohttpd.so.12+0x118af) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #5 MHD_epoll daemon.c (libmicrohttpd.so.12+0x194ad) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #6 MHD_polling_thread daemon.c (libmicrohttpd.so.12+0x1a02e) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #7 named_thread_starter mhd_threads.c (libmicrohttpd.so.12+0x24515) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)

  Previous write of size 8 at 0x7b1400003550 by thread T2:
    #0 operator new(unsigned long) /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/tsan_new_delete.cpp:64:3 (lt-basic+0xea6e5)
    #1 std::pair<std::_Rb_tree_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>, bool> std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>>::_M_emplace_unique<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, httpserver::http_resource*>>(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, httpserver::http_resource*>&&) <null> (libhttpserver.so.0+0x2669a)

  Location is heap block of size 72 at 0x7b1400003520 allocated by thread T2:
    #0 operator new(unsigned long) /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/tsan_new_delete.cpp:64:3 (lt-basic+0xea6e5)
    #1 std::pair<std::_Rb_tree_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>, bool> std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, httpserver::http_resource*>>>::_M_emplace_unique<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, httpserver::http_resource*>>(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, httpserver::http_resource*>&&) <null> (libhttpserver.so.0+0x2669a)

  Thread T1 'MHD-single' (tid=21814, running) created by main thread at:
    #0 pthread_create /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:1048:3 (lt-basic+0x2db2f)
    #1 MHD_create_thread_ <null> (libmicrohttpd.so.12+0x2443d) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #2 MHD_create_named_thread_ <null> (libmicrohttpd.so.12+0x24602) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #3 MHD_start_daemon_va <null> (libmicrohttpd.so.12+0x1e4de) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #4 MHD_start_daemon <null> (libmicrohttpd.so.12+0x1a1d6) (BuildId: 72677d816e65dce550957833f9aea14ac2e0e4c8)
    #5 httpserver::webserver::start(bool) <null> (libhttpserver.so.0+0x20090)

  Thread T2 (tid=21815, running) created by main thread at:
    #0 pthread_create /clang-16.0.1/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:1048:3 (lt-basic+0x2db2f)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xed8f9) (BuildId: 06e9553aa6e15b32e77410de83bbdd7d208a620d)

SUMMARY: ThreadSanitizer: data race (/home/florian/work/libhttpserver/build/src/.libs/libhttpserver.so.0+0x227b2) in httpserver::webserver::finalize_answer(MHD_Connection*, httpserver::details::modded_request*, char const*)
==================
- Time spent during "thread_safety": 10702.4 ms
==================

Reproduces how often: 100%

Versions

  • OS version : Linux 5f8a17221539 5.19.0-46-generic Cannot register multiple endpoints #47~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Jun 21 15:35:31 UTC 2 x86_64 GNU/Linux
  • libhttpserver version: master (d249ba6) compiled locally
  • libmicrohttpd version: 0.9.64 compiled locally

Metadata

Metadata

Assignees

Labels

bugConfirmed bugs or reports that are very likely to be bugs.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions