[compiler-rt] r202820 - tsan: fix pthread_cond interceptors

Dmitry Vyukov dvyukov at google.com
Tue Mar 4 03:11:40 PST 2014


Author: dvyukov
Date: Tue Mar  4 05:11:40 2014
New Revision: 202820

URL: http://llvm.org/viewvc/llvm-project?rev=202820&view=rev
Log:
tsan: fix pthread_cond interceptors
currently tsan hangs when linked with a shared library linked against an old version of pthread
this change is another attempt to fix pthread_cond interceptors in different scenarios
see the comment for implementation details


Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc?rev=202820&r1=202819&r2=202820&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc Tue Mar  4 05:11:40 2014
@@ -2431,52 +2431,93 @@ INTERCEPTOR(int, pthread_mutex_unlock, v
 #endif
 
 #if SANITIZER_INTERCEPT_PTHREAD_COND
+// nptl implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
+// pthread_cond_t has different size in the different versions.
+// We can't simply always call new REAL functions from interceptors,
+// because they will corrupt memory after pthread_cond_t (old cond is smaller).
+// We can't simply always call old REAL functions from interceptors,
+// because they do not support all the features (e.g. waiting against
+// CLOCK_REALTIME).
+// Proper handling would require to have 2 versions of interceptors as well.
+// But this is messy, in particular requires linker scripts when sanitizer
+// runtime is linked into a shared library.
+// Instead we do the following trick.
+static void *init_cond(void *c, bool force = false) {
+  // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
+  // So we allocate additional memory on the side large enough to hold
+  // any pthread_cond_t object. Always call new REAL functions, but pass
+  // the aux object to them.
+  // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
+  // first word of pthread_cond_t to zero.
+  atomic_uintptr_t *p = (atomic_uintptr_t*)c;
+  uptr cond = atomic_load(p, memory_order_acquire);
+  if (!force && cond != 0)
+    return (void*)cond;
+  void *newcond = WRAP(malloc)(pthread_cond_t_sz);
+  internal_memset(newcond, 0, pthread_cond_t_sz);
+  if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
+      memory_order_acq_rel))
+    return newcond;
+  WRAP(free)(newcond);
+  return (void*)cond;
+}
+
 INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+  void *cond = init_cond(c, true);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a);
-  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, 1);
-  return REAL(pthread_cond_init)(c, a);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, cond, a);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr));
+  return REAL(pthread_cond_init)(cond, a);
 }
 
 INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+  void *cond = init_cond(c);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, cond, m);
   COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
-  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, 1);
-  int res = REAL(pthread_cond_wait)(c, m);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
+  int res = REAL(pthread_cond_wait)(cond, m);
   COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
   return res;
 }
 
 INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+  void *cond = init_cond(c);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait, c, m, abstime);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait, cond, m, abstime);
   COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
-  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, 1);
-  int res = REAL(pthread_cond_timedwait)(c, m, abstime);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
+  int res = REAL(pthread_cond_timedwait)(cond, m, abstime);
   COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
   return res;
 }
 
 INTERCEPTOR(int, pthread_cond_signal, void *c) {
+  void *cond = init_cond(c);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c);
-  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, 1);
-  return REAL(pthread_cond_signal)(c);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, cond);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
+  return REAL(pthread_cond_signal)(cond);
 }
 
 INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+  void *cond = init_cond(c);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c);
-  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, 1);
-  return REAL(pthread_cond_broadcast)(c);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, cond);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
+  return REAL(pthread_cond_broadcast)(cond);
 }
 
 INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+  void *cond = init_cond(c);
   void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy, c);
-  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, 1);
-  return REAL(pthread_cond_destroy)(c);
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy, cond);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr));
+  int res = REAL(pthread_cond_destroy)(cond);
+  // Free our aux cond and zero the pointer to not leave dangling pointers.
+  WRAP(free)(cond);
+  atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
+  return res;
 }
 
 #define INIT_PTHREAD_COND \





More information about the llvm-commits mailing list