summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2013-11-28 09:09:42 +0000
committerDmitry Vyukov <dvyukov@google.com>2013-11-28 09:09:42 +0000
commit575bd6bb5d8fd652f7d18aa78c7bcb50d72447b5 (patch)
tree5e4b4ccad0f53378d33b7c7669ef12c873df92e3
parentb8a696aa1e749d6cfa23553b3dc6afd59482f826 (diff)
downloadcompiler-rt-575bd6bb5d8fd652f7d18aa78c7bcb50d72447b5.tar.gz
tsan: add support for passing file descriptors over UNIX domain sockets
tsan was missing new fd's arrived from recvmsg(), and thus was reporting false positives due to missed synchronization on the fd's git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@195914 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc10
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc12
-rw-r--r--lib/tsan/rtl/tsan_platform.h1
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc24
4 files changed, 46 insertions, 1 deletions
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 9e43e8d14..1668bb0fa 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -25,6 +25,7 @@
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
// COMMON_INTERCEPTOR_MUTEX_REPAIR
// COMMON_INTERCEPTOR_SET_PTHREAD_NAME
+// COMMON_INTERCEPTOR_HANDLE_RECVMSG
//===----------------------------------------------------------------------===//
#include "interception/interception.h"
#include "sanitizer_platform_interceptors.h"
@@ -55,6 +56,10 @@
#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {}
#endif
+#ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG
+#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg))
+#endif
+
#if SANITIZER_INTERCEPT_STRCMP
static inline int CharCmpX(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
@@ -1402,7 +1407,10 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg,
SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
if (res >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
- if (msg) write_msghdr(ctx, msg, res);
+ if (msg) {
+ write_msghdr(ctx, msg, res);
+ COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg);
+ }
}
return res;
}
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 18637b591..f02b97d71 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -1834,6 +1834,14 @@ struct TsanInterceptorContext {
const uptr pc;
};
+static void HandleRecvmsg(ThreadState *thr, uptr pc,
+ __sanitizer_msghdr *msg) {
+ int fds[64];
+ int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds));
+ for (int i = 0; i < cnt; i++)
+ FdEventCreate(thr, pc, fds[i]);
+}
+
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getpwuid_r() calls fopen())
#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
@@ -1900,6 +1908,10 @@ struct TsanInterceptorContext {
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
+ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, msg)
+
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define TSAN_SYSCALL() \
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 32e22ba60..837776715 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -162,6 +162,7 @@ void internal_start_thread(void(*func)(void*), void *arg);
// Guesses with high probability, may yield both false positives and negatives.
bool IsGlobalVar(uptr addr);
int ExtractResolvFDs(void *state, int *fds, int nfd);
+int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index 906b5dc73..282b2982f 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -34,6 +34,7 @@
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
+#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
@@ -347,6 +348,9 @@ bool IsGlobalVar(uptr addr) {
}
#ifndef TSAN_GO
+// Extract file descriptors passed to glibc internal __res_iclose function.
+// This is required to properly "close" the fds, because we do not see internal
+// closes within glibc. The code is a pure hack.
int ExtractResolvFDs(void *state, int *fds, int nfd) {
int cnt = 0;
__res_state *statp = (__res_state*)state;
@@ -356,6 +360,26 @@ int ExtractResolvFDs(void *state, int *fds, int nfd) {
}
return cnt;
}
+
+// Extract file descriptors passed via UNIX domain sockets.
+// This is requried to properly handle "open" of these fds.
+// see 'man recvmsg' and 'man 3 cmsg'.
+int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
+ int res = 0;
+ msghdr *msg = (msghdr*)msgp;
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+ for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+ continue;
+ int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]);
+ for (int i = 0; i < n; i++) {
+ fds[res++] = ((int*)CMSG_DATA(cmsg))[i];
+ if (res == nfd)
+ return res;
+ }
+ }
+ return res;
+}
#endif