llvm-project/compiler-rt/test/tsan/signal_in_read.c
Pavel Labath cf1319f9c6
[compiler-rt] Mark more calls as blocking (#77789)
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.
2024-03-11 07:44:26 +01:00

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