-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[tsan] Add callbacks for futex syscalls and mark them as blocking on …
…tsan (#86537) Fixes #83844. This PR adds callbacks to mark futex syscalls as blocking. Unfortunately we didn't have a mechanism before to mark syscalls as a blocking call, so I had to implement it, but it mostly reuses the `BlockingCall` implementation [here](https://github.com/llvm/llvm-project/blob/96819daa3d095cf9f662e0229dc82eaaa25480e8/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp#L362-L380). The issue includes some information but this issue was discovered because Rust uses futexes directly. So most likely we need to update Rust as well to use these callbacks. Also see the latest comments in #85188 for some context. I also sent another PR #84162 to mark `pthread_*_lock` calls as blocking.
- Loading branch information
Showing
4 changed files
with
178 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// RUN: %clang_tsan %s -lstdc++ -o %t && %run %t 2>&1 | FileCheck %s | ||
|
||
#include "test.h" | ||
#include <errno.h> | ||
#include <linux/futex.h> | ||
#include <pthread.h> | ||
#include <signal.h> | ||
#include <stdio.h> | ||
#include <sys/syscall.h> | ||
|
||
#include <cassert> | ||
#include <stdexcept> | ||
#include <thread> | ||
|
||
#include <sanitizer/linux_syscall_hooks.h> | ||
|
||
int futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, | ||
int *uaddr2, int val3) { | ||
__sanitizer_syscall_pre_futex(uaddr, futex_op, val, timeout, uaddr2, val3); | ||
int result = syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); | ||
__sanitizer_syscall_post_futex(result, uaddr, futex_op, val, timeout, uaddr2, | ||
val3); | ||
return result; | ||
} | ||
|
||
// Simple mutex implementation using futex. | ||
class Mutex { | ||
public: | ||
Mutex() : value(0) {} | ||
|
||
void lock() { | ||
int c; | ||
while ((c = __sync_val_compare_and_swap(&value, 0, 1)) != 0) { | ||
if (c != 1) | ||
continue; | ||
int r = futex(&value, FUTEX_WAIT_PRIVATE, 1, nullptr, nullptr, 0); | ||
if (r == -1 && errno != EAGAIN) { | ||
fprintf(stderr, "futex wait error\n"); | ||
abort(); | ||
} | ||
} | ||
} | ||
|
||
void unlock() { | ||
value = 0; | ||
int r = futex(&value, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); | ||
if (r == -1) { | ||
fprintf(stderr, "futex wake error\n"); | ||
abort(); | ||
} | ||
} | ||
|
||
private: | ||
int value; | ||
}; | ||
|
||
Mutex mutex; | ||
|
||
void *Thread(void *x) { | ||
// fprintf(stderr, "canova here thread 0\n"); | ||
// Waiting for the futex. | ||
mutex.lock(); | ||
// fprintf(stderr, "canova here thread 1\n"); | ||
// Finished waiting. | ||
return nullptr; | ||
} | ||
|
||
static void SigprofHandler(int signal, siginfo_t *info, void *context) { | ||
// fprintf(stderr, "canova here sigprof handler\n"); | ||
// Unlock the futex. | ||
mutex.unlock(); | ||
} | ||
|
||
void InstallSignalHandler() { | ||
struct sigaction sa; | ||
sa.sa_sigaction = SigprofHandler; | ||
sigemptyset(&sa.sa_mask); | ||
sa.sa_flags = SA_RESTART | SA_SIGINFO; | ||
if (sigaction(SIGPROF, &sa, 0) != 0) { | ||
fprintf(stderr, "failed to install signal handler\n"); | ||
abort(); | ||
} | ||
} | ||
|
||
int main() { | ||
alarm(60); // Kill the test if it hangs. | ||
|
||
// Install the signal handler | ||
InstallSignalHandler(); | ||
|
||
// Lock the futex at first so the other thread will wait for it. | ||
mutex.lock(); | ||
|
||
// Create the thread to wait for the futex. | ||
pthread_t thread; | ||
pthread_create(&thread, NULL, Thread, NULL); | ||
|
||
// Just waiting a bit to make sure the thead is at the FUTEX_WAIT_PRIVATE | ||
// syscall. | ||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
||
// Send the signal to the other thread, which will send the futex wake | ||
// syscall. | ||
int r = pthread_kill(thread, SIGPROF); | ||
assert(r == 0); | ||
|
||
// Futex should be notified and the thread should be able to continue. | ||
pthread_join(thread, NULL); | ||
|
||
// Exiting successfully. | ||
fprintf(stderr, "PASS\n"); | ||
return 0; | ||
} | ||
|
||
// CHECK-NOT: WARNING: ThreadSanitizer: | ||
// CHECK: PASS |