llvm / llvm/llvm-project

[TSan] Missing interceptors for flockfile/ftrylockfile/funlockfile cause false positive data race reports

Open
#203,451 2 comments 1 reaction 0 assignees View on GitHub
compiler-rt:tsan false-positive
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

TSan does not intercept `flockfile`, `ftrylockfile`, or `funlockfile`. Observed code that correctly serializes `FILE` access using these POSIX stdio locking primitives is reported as a data race because TSan cannot establish the happens-before relationship through them.

## Reproducible Example

```cpp
#include
#include

int main() {
int shared_data{} ;
std::jthread a([&]{
flockfile(stdout);
shared_data++; // correctly protected
funlockfile(stdout);
});
std::jthread b([&]{
flockfile(stdout);
shared_data++; // correctly protected
funlockfile(stdout);
});
}
```

Compiler flags
```
-fsanitize=thread
```

[Compiler Explorer example](https://godbolt.org/z/zoM1f7GYz)

## TSan Warning Description

TSan reports a data race on shared data despite both threads holding the file lock when accessing it. The code is accurate, `flockfile` is a POSIX-specified lock per file stream.

## Root Cause
[compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp](https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp) does not contain interceptors for `flockfile`, `ftrylockfile`, or `funlockfile`. TSan therefore sees no synchronization between threads that coordinate through these calls.

## Impact
libstdc++'s `std::print`/`std::println` implementation on Glibc platforms uses `flockfile` for the duration of each print call and is falsely reported as a data race under TSan.

## Suggested fix
Add interceptors analogous to the existing pthread mutex ones:

```cpp
TSAN_INTERCEPTOR(void, flockfile, FILE *file) {
SCOPED_TSAN_INTERCEPTOR(flockfile, file);
REAL(flockfile)(file);
MutexPostLock(thr, pc, (uptr)file,
MutexFlagNotStatic | MutexFlagWriteReentrant);
}
TSAN_INTERCEPTOR(int, ftrylockfile, FILE *file) {
SCOPED_TSAN_INTERCEPTOR(ftrylockfile, file);
int ret = REAL(ftrylockfile)(file);
if (ret == 0)
MutexPostLock(thr, pc, (uptr)file,
MutexFlagNotStatic | MutexFlagWriteReentrant | MutexFlagTryLock);
return ret;
}
TSAN_INTERCEPTOR(void, funlockfile, FILE *file) {
SCOPED_TSAN_INTERCEPTOR(funlockfile, file);
MutexPreUnlock(thr, pc, (uptr)file);
REAL(funlockfile)(file);
}
```

I intend to submit a PR with this fix and a corresponding test.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.