mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 21:56:36 +00:00

If we're in a blocking call, we need to run the signal immediately, as the call may not return for a very long time (if ever). Not running the handler can cause deadlocks if the rest of the program waits (in one way or another) for the signal handler to execute. I've gone through the list of functions in sanitizer_common_interceptors and marked as blocking those that I know can block, but I don't claim the list to be exhaustive. In particular, I did not mark libc FILE* functions as blocking, because these can end up calling user functions. To do that correctly, /I think/ it would be necessary to clear the "is in blocking call" flag inside the fopencookie wrappers. The test for the bug (deadlock) uses the read call (which is the one that I ran into originally), but the same kind of test could be written for any other blocking syscall.
60 lines
1.3 KiB
C
60 lines
1.3 KiB
C
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
|
|
|
|
#include "test.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
static int SignalPipeFd[] = {-1, -1};
|
|
static int BlockingPipeFd[] = {-1, -1};
|
|
|
|
static void Handler(int _) { assert(write(SignalPipeFd[1], ".", 1) == 1); }
|
|
|
|
static void *ThreadFunc(void *_) {
|
|
char C;
|
|
assert(read(BlockingPipeFd[0], &C, sizeof(C)) == 1);
|
|
assert(C == '.');
|
|
return 0;
|
|
}
|
|
|
|
int main() {
|
|
alarm(60); // Kill the test if it hangs.
|
|
|
|
assert(pipe(SignalPipeFd) == 0);
|
|
assert(pipe(BlockingPipeFd) == 0);
|
|
|
|
struct sigaction act;
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_flags = SA_RESTART;
|
|
act.sa_handler = Handler;
|
|
assert(sigaction(SIGUSR1, &act, 0) == 0);
|
|
|
|
pthread_t Thr;
|
|
assert(pthread_create(&Thr, 0, ThreadFunc, 0) == 0);
|
|
|
|
// Give the thread enough time to block in the read call.
|
|
usleep(1000000);
|
|
|
|
// Signal the thread, this should run the signal handler and unblock the read
|
|
// below.
|
|
pthread_kill(Thr, SIGUSR1);
|
|
char C;
|
|
assert(read(SignalPipeFd[0], &C, 1) == 1);
|
|
|
|
// Unblock the thread and join it.
|
|
assert(write(BlockingPipeFd[1], &C, 1) == 1);
|
|
void *_ = 0;
|
|
assert(pthread_join(Thr, &_) == 0);
|
|
|
|
fprintf(stderr, "PASS\n");
|
|
return 0;
|
|
}
|
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
|
// CHECK: PASS
|