[compiler-rt] 09910aa - [tsan] fcntl interceptor with F_DUPFD and F_DUPFD_CLOEXEC support (#221064)

via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 4 23:14:30 PDT 2026


Author: Mike Aizatsky
Date: 2026-10-05T06:14:23Z
New Revision: 09910aa044808dfbfe1e0f96b5e39260789212fb

URL: https://github.com/llvm/llvm-project/commit/09910aa044808dfbfe1e0f96b5e39260789212fb
DIFF: https://github.com/llvm/llvm-project/commit/09910aa044808dfbfe1e0f96b5e39260789212fb.diff

LOG: [tsan] fcntl interceptor with F_DUPFD and F_DUPFD_CLOEXEC support (#221064)

behaves like dup

fixes https://github.com/rust-lang/rust/issues/130037 and bunch of
others

Added: 
    compiler-rt/test/tsan/Linux/fd_fcntl_dupfd_cloexec_norace.cpp

Modified: 
    compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index b2ba666741e89..ed16e15a25edd 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -1827,29 +1827,64 @@ TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
 #define TSAN_MAYBE_INTERCEPT_CREAT64
 #endif
 
-TSAN_INTERCEPTOR(int, dup, int oldfd) {
-  SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
-  int newfd = REAL(dup)(oldfd);
+static int HandleDupResult(ThreadState* thr, uptr pc, int oldfd, int newfd,
+                           bool write) {
   if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
-    FdDup(thr, pc, oldfd, newfd, true);
+    FdDup(thr, pc, oldfd, newfd, write);
   return newfd;
 }
 
+#if SANITIZER_LINUX
+// These values are part of the Linux UAPI and are the same on all Linux
+// architectures.
+static const int kF_DUPFD = 0;
+static const int kF_DUPFD_CLOEXEC = 1030;
+
+// fcntl accepts either an int or a pointer as its optional argument.  Reading
+// it as uptr follows libc's implementation strategy and preserves either value
+// on the ABIs supported by TSan.
+#  define TSAN_FCNTL_INTERCEPTOR(func)                       \
+    TSAN_INTERCEPTOR(int, func, int oldfd, int cmd, ...) {   \
+      va_list ap;                                            \
+      va_start(ap, cmd);                                     \
+      uptr arg = va_arg(ap, uptr);                           \
+      va_end(ap);                                            \
+      SCOPED_TSAN_INTERCEPTOR(func, oldfd, cmd, arg);        \
+      int newfd = REAL(func)(oldfd, cmd, arg);               \
+      if (cmd == kF_DUPFD || cmd == kF_DUPFD_CLOEXEC)        \
+        return HandleDupResult(thr, pc, oldfd, newfd, true); \
+      return newfd;                                          \
+    }
+
+TSAN_FCNTL_INTERCEPTOR(fcntl)
+#  if SANITIZER_INTERCEPT_FCNTL64
+TSAN_FCNTL_INTERCEPTOR(fcntl64)
+#    define TSAN_MAYBE_INTERCEPT_FCNTL64 TSAN_INTERCEPT(fcntl64)
+#  else
+#    define TSAN_MAYBE_INTERCEPT_FCNTL64
+#  endif
+#  undef TSAN_FCNTL_INTERCEPTOR
+#  define TSAN_MAYBE_INTERCEPT_FCNTL TSAN_INTERCEPT(fcntl)
+#else
+#  define TSAN_MAYBE_INTERCEPT_FCNTL
+#  define TSAN_MAYBE_INTERCEPT_FCNTL64
+#endif
+
+TSAN_INTERCEPTOR(int, dup, int oldfd) {
+  SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
+  return HandleDupResult(thr, pc, oldfd, REAL(dup)(oldfd), true);
+}
+
 TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
   SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
-  int newfd2 = REAL(dup2)(oldfd, newfd);
-  if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
-    FdDup(thr, pc, oldfd, newfd2, false);
-  return newfd2;
+  return HandleDupResult(thr, pc, oldfd, REAL(dup2)(oldfd, newfd), false);
 }
 
 #if !SANITIZER_APPLE
 TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
   SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
-  int newfd2 = REAL(dup3)(oldfd, newfd, flags);
-  if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
-    FdDup(thr, pc, oldfd, newfd2, false);
-  return newfd2;
+  return HandleDupResult(thr, pc, oldfd, REAL(dup3)(oldfd, newfd, flags),
+                         false);
 }
 #endif
 
@@ -3142,6 +3177,8 @@ void InitializeInterceptors() {
   TSAN_MAYBE_INTERCEPT_OPEN64;
   TSAN_INTERCEPT(creat);
   TSAN_MAYBE_INTERCEPT_CREAT64;
+  TSAN_MAYBE_INTERCEPT_FCNTL;
+  TSAN_MAYBE_INTERCEPT_FCNTL64;
   TSAN_INTERCEPT(dup);
   TSAN_INTERCEPT(dup2);
   TSAN_INTERCEPT(dup3);

diff  --git a/compiler-rt/test/tsan/Linux/fd_fcntl_dupfd_cloexec_norace.cpp b/compiler-rt/test/tsan/Linux/fd_fcntl_dupfd_cloexec_norace.cpp
new file mode 100644
index 0000000000000..2bd9928934e01
--- /dev/null
+++ b/compiler-rt/test/tsan/Linux/fd_fcntl_dupfd_cloexec_norace.cpp
@@ -0,0 +1,46 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int value;
+int duplicate_write_fd;
+
+void *writer(void *) {
+  value = 1;
+  if (write(duplicate_write_fd, ".", 1) != 1)
+    abort();
+  return nullptr;
+}
+
+int main() {
+  int pipe_fds[2];
+  if (pipe2(pipe_fds, O_CLOEXEC) != 0)
+    abort();
+
+  duplicate_write_fd = fcntl(pipe_fds[1], F_DUPFD_CLOEXEC, 0);
+  if (duplicate_write_fd < 0)
+    abort();
+
+  pthread_t thread;
+  pthread_create(&thread, nullptr, writer, nullptr);
+
+  char byte;
+  if (read(pipe_fds[0], &byte, 1) != 1)
+    abort();
+  if (value != 1)
+    abort();
+
+  pthread_join(thread, nullptr);
+  close(duplicate_write_fd);
+  close(pipe_fds[0]);
+  close(pipe_fds[1]);
+  fprintf(stderr, "DONE\n");
+  return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE


        


More information about the llvm-commits mailing list