google / sanitizers

AddressSanitizer, ThreadSanitizer, MemorySanitizer
Other
11.41k stars 1.03k forks source link

[tsan] False positive when reading from a pipe in io_uring #1729

Open cmazakas opened 7 months ago

cmazakas commented 7 months ago

Given the following code, I notice that if I read from the pipe using io_uring, I get a (seeming) false positive from tsan

Consider:

#include <thread>
#include <cassert>
#include <memory>
#include <iostream>
#include <chrono>

#include <unistd.h>
#include <liburing.h>

int main() {
  io_uring ring;
  io_uring_queue_init(8, &ring, 0);

  int pipe_fds[2]={-1,-1};
  int ret=-1;
  ret=pipe(pipe_fds);
  if(ret==-1){return errno;}

  int x=-1;
  std::jthread t([&x,fd=pipe_fds[1]]()mutable{
    auto p=std::make_unique<int>(1337);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    auto buf=p.release();
    *buf=7331;
    x=INT_MAX;
    write(fd,&buf,sizeof(buf));
  });

  int* q=nullptr;

  auto sqe=io_uring_get_sqe(&ring);
  io_uring_prep_read(sqe,pipe_fds[0],&q,sizeof(q),0);
  io_uring_submit(&ring);

  io_uring_cqe* cqe=nullptr;
  io_uring_wait_cqe(&ring,&cqe);
  io_uring_cqe_seen(&ring,cqe);

  //read(pipe_fds[0],&q,sizeof(q));
  std::unique_ptr<int> p(q);
  std::cout<<*p<<std::endl;
  std::cout<<x<<std::endl;

  io_uring_queue_exit(&ring);
}

produces:

exbigboss@exbigboss-ubuntu ~/cpp/tsan
❯ uname -a
Linux exbigboss-ubuntu 6.5.0-17-generic #17-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 11 14:01:59 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

exbigboss@exbigboss-ubuntu ~/cpp/tsan 
❯ clang++-17 -fsanitize=thread -std=c++20 a.cpp ~/cpp/__install__/lib/liburing.a -I ~/cpp/__install__/include/ && ./a.out
==================
WARNING: ThreadSanitizer: data race (pid=164382)
  Read of size 4 at 0x7b0400000800 by main thread:
    #0 main <null> (a.out+0xea978) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)

  Previous write of size 8 at 0x7b0400000800 by thread T1:
    #0 operator new(unsigned long) <null> (a.out+0xe985b) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #1 std::__detail::_MakeUniq<int>::__single_object std::make_unique<int, int>(int&&) <null> (a.out+0xee296) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #2 main::$_0::operator()() a.cpp (a.out+0xebc48) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #3 void std::__invoke_impl<void, main::$_0>(std::__invoke_other, main::$_0&&) a.cpp (a.out+0xebbd5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #4 std::__invoke_result<main::$_0>::type std::__invoke<main::$_0>(main::$_0&&) a.cpp (a.out+0xebb45) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #5 void std::thread::_Invoker<std::tuple<main::$_0>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) a.cpp (a.out+0xebafd) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #6 std::thread::_Invoker<std::tuple<main::$_0>>::operator()() a.cpp (a.out+0xebaa5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_0>>>::_M_run() a.cpp (a.out+0xeb969) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #8 execute_native_thread_routine /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xe6332) (BuildId: 102661775a59c09123f226bc3021556fd42bc563)

  Location is heap block of size 4 at 0x7b0400000800 allocated by thread T1:
    #0 operator new(unsigned long) <null> (a.out+0xe985b) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #1 std::__detail::_MakeUniq<int>::__single_object std::make_unique<int, int>(int&&) <null> (a.out+0xee296) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #2 main::$_0::operator()() a.cpp (a.out+0xebc48) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #3 void std::__invoke_impl<void, main::$_0>(std::__invoke_other, main::$_0&&) a.cpp (a.out+0xebbd5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #4 std::__invoke_result<main::$_0>::type std::__invoke<main::$_0>(main::$_0&&) a.cpp (a.out+0xebb45) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #5 void std::thread::_Invoker<std::tuple<main::$_0>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) a.cpp (a.out+0xebafd) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #6 std::thread::_Invoker<std::tuple<main::$_0>>::operator()() a.cpp (a.out+0xebaa5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_0>>>::_M_run() a.cpp (a.out+0xeb969) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #8 execute_native_thread_routine /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xe6332) (BuildId: 102661775a59c09123f226bc3021556fd42bc563)

  Thread T1 (tid=164384, finished) created by main thread at:
    #0 pthread_create <null> (a.out+0x605ff) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #1 __gthread_create /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:663:35 (libstdc++.so.6+0xe6408) (BuildId: 102661775a59c09123f226bc3021556fd42bc563)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xe6408)
    #3 std::thread std::jthread::_S_create<main::$_0>(std::stop_source&, main::$_0&&) a.cpp (a.out+0xeb6a1) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #4 std::jthread::jthread<main::$_0, void>(main::$_0&&) a.cpp (a.out+0xeab25) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #5 main <null> (a.out+0xea862) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)

SUMMARY: ThreadSanitizer: data race (/home/exbigboss/cpp/tsan/a.out+0xea978) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9) in main
==================
7331
/usr/bin/llvm-symbolizer-17: error: '[stack]': No such file or directory
==================
WARNING: ThreadSanitizer: data race (pid=164382)
  Read of size 4 at 0x7ffe8cd593d0 by main thread:
    #0 main <null> (a.out+0xea9bd) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)

  Previous write of size 4 at 0x7ffe8cd593d0 by thread T1:
    #0 main::$_0::operator()() a.cpp (a.out+0xebcdc) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #1 void std::__invoke_impl<void, main::$_0>(std::__invoke_other, main::$_0&&) a.cpp (a.out+0xebbd5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #2 std::__invoke_result<main::$_0>::type std::__invoke<main::$_0>(main::$_0&&) a.cpp (a.out+0xebb45) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #3 void std::thread::_Invoker<std::tuple<main::$_0>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) a.cpp (a.out+0xebafd) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #4 std::thread::_Invoker<std::tuple<main::$_0>>::operator()() a.cpp (a.out+0xebaa5) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_0>>>::_M_run() a.cpp (a.out+0xeb969) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #6 execute_native_thread_routine /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:104:18 (libstdc++.so.6+0xe6332) (BuildId: 102661775a59c09123f226bc3021556fd42bc563)

  Location is stack of main thread.

  Location is global '??' at 0x7ffe8cd3a000 ([stack]+0x1f3d0)

  Thread T1 (tid=164384, finished) created by main thread at:
    #0 pthread_create <null> (a.out+0x605ff) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #1 __gthread_create /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:663:35 (libstdc++.so.6+0xe6408) (BuildId: 102661775a59c09123f226bc3021556fd42bc563)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /build/gcc-13-XYspKM/gcc-13-13.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:172:37 (libstdc++.so.6+0xe6408)
    #3 std::thread std::jthread::_S_create<main::$_0>(std::stop_source&, main::$_0&&) a.cpp (a.out+0xeb6a1) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #4 std::jthread::jthread<main::$_0, void>(main::$_0&&) a.cpp (a.out+0xeab25) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)
    #5 main <null> (a.out+0xea862) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9)

SUMMARY: ThreadSanitizer: data race (/home/exbigboss/cpp/tsan/a.out+0xea9bd) (BuildId: c37a21adbd15c7d0b664b44dd7a19b1d98583db9) in main
==================
2147483647
ThreadSanitizer: reported 2 warnings

However, commenting out the pipe read from io_uring and instead using the blocking read() call directly does not produce these warnings.