1//===-- dd_interceptors.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "dd_rtl.h"
10#include "interception/interception.h"
11#include "sanitizer_common/sanitizer_procmaps.h"
12#include <pthread.h>
13#include <stdlib.h>
14
15using namespace __dsan;
16
17__attribute__((tls_model("initial-exec")))
18static __thread Thread *thr;
19__attribute__((tls_model("initial-exec")))
20static __thread volatile int initing;
21static bool inited;
22static uptr g_data_start;
23static uptr g_data_end;
24
25static bool InitThread() {
26  if (initing)
27    return false;
28  if (thr != 0)
29    return true;
30  initing = true;
31  if (!inited) {
32    inited = true;
33    Initialize();
34  }
35  thr = (Thread*)InternalAlloc(sizeof(*thr));
36  internal_memset(thr, 0, sizeof(*thr));
37  ThreadInit(thr);
38  initing = false;
39  return true;
40}
41
42INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) {
43  InitThread();
44  MutexDestroy(thr, (uptr)m);
45  return REAL(pthread_mutex_destroy)(m);
46}
47
48INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) {
49  InitThread();
50  MutexBeforeLock(thr, (uptr)m, true);
51  int res = REAL(pthread_mutex_lock)(m);
52  MutexAfterLock(thr, (uptr)m, true, false);
53  return res;
54}
55
56INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) {
57  InitThread();
58  int res = REAL(pthread_mutex_trylock)(m);
59  if (res == 0)
60    MutexAfterLock(thr, (uptr)m, true, true);
61  return res;
62}
63
64INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) {
65  InitThread();
66  MutexBeforeUnlock(thr, (uptr)m, true);
67  return REAL(pthread_mutex_unlock)(m);
68}
69
70INTERCEPTOR(int, pthread_spin_destroy, pthread_spinlock_t *m) {
71  InitThread();
72  int res = REAL(pthread_spin_destroy)(m);
73  MutexDestroy(thr, (uptr)m);
74  return res;
75}
76
77INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *m) {
78  InitThread();
79  MutexBeforeLock(thr, (uptr)m, true);
80  int res = REAL(pthread_spin_lock)(m);
81  MutexAfterLock(thr, (uptr)m, true, false);
82  return res;
83}
84
85INTERCEPTOR(int, pthread_spin_trylock, pthread_spinlock_t *m) {
86  InitThread();
87  int res = REAL(pthread_spin_trylock)(m);
88  if (res == 0)
89    MutexAfterLock(thr, (uptr)m, true, true);
90  return res;
91}
92
93INTERCEPTOR(int, pthread_spin_unlock, pthread_spinlock_t *m) {
94  InitThread();
95  MutexBeforeUnlock(thr, (uptr)m, true);
96  return REAL(pthread_spin_unlock)(m);
97}
98
99INTERCEPTOR(int, pthread_rwlock_destroy, pthread_rwlock_t *m) {
100  InitThread();
101  MutexDestroy(thr, (uptr)m);
102  return REAL(pthread_rwlock_destroy)(m);
103}
104
105INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *m) {
106  InitThread();
107  MutexBeforeLock(thr, (uptr)m, false);
108  int res = REAL(pthread_rwlock_rdlock)(m);
109  MutexAfterLock(thr, (uptr)m, false, false);
110  return res;
111}
112
113INTERCEPTOR(int, pthread_rwlock_tryrdlock, pthread_rwlock_t *m) {
114  InitThread();
115  int res = REAL(pthread_rwlock_tryrdlock)(m);
116  if (res == 0)
117    MutexAfterLock(thr, (uptr)m, false, true);
118  return res;
119}
120
121INTERCEPTOR(int, pthread_rwlock_timedrdlock, pthread_rwlock_t *m,
122    const timespec *abstime) {
123  InitThread();
124  int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
125  if (res == 0)
126    MutexAfterLock(thr, (uptr)m, false, true);
127  return res;
128}
129
130INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *m) {
131  InitThread();
132  MutexBeforeLock(thr, (uptr)m, true);
133  int res = REAL(pthread_rwlock_wrlock)(m);
134  MutexAfterLock(thr, (uptr)m, true, false);
135  return res;
136}
137
138INTERCEPTOR(int, pthread_rwlock_trywrlock, pthread_rwlock_t *m) {
139  InitThread();
140  int res = REAL(pthread_rwlock_trywrlock)(m);
141  if (res == 0)
142    MutexAfterLock(thr, (uptr)m, true, true);
143  return res;
144}
145
146INTERCEPTOR(int, pthread_rwlock_timedwrlock, pthread_rwlock_t *m,
147    const timespec *abstime) {
148  InitThread();
149  int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
150  if (res == 0)
151    MutexAfterLock(thr, (uptr)m, true, true);
152  return res;
153}
154
155INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *m) {
156  InitThread();
157  MutexBeforeUnlock(thr, (uptr)m, true);  // note: not necessary write unlock
158  return REAL(pthread_rwlock_unlock)(m);
159}
160
161static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) {
162  atomic_uintptr_t *p = (atomic_uintptr_t*)c;
163  uptr cond = atomic_load(p, memory_order_acquire);
164  if (!force && cond != 0)
165    return (pthread_cond_t*)cond;
166  void *newcond = malloc(sizeof(pthread_cond_t));
167  internal_memset(newcond, 0, sizeof(pthread_cond_t));
168  if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
169      memory_order_acq_rel))
170    return (pthread_cond_t*)newcond;
171  free(newcond);
172  return (pthread_cond_t*)cond;
173}
174
175INTERCEPTOR(int, pthread_cond_init, pthread_cond_t *c,
176    const pthread_condattr_t *a) {
177  InitThread();
178  pthread_cond_t *cond = init_cond(c, true);
179  return REAL(pthread_cond_init)(cond, a);
180}
181
182INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *c, pthread_mutex_t *m) {
183  InitThread();
184  pthread_cond_t *cond = init_cond(c);
185  MutexBeforeUnlock(thr, (uptr)m, true);
186  MutexBeforeLock(thr, (uptr)m, true);
187  int res = REAL(pthread_cond_wait)(cond, m);
188  MutexAfterLock(thr, (uptr)m, true, false);
189  return res;
190}
191
192INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *c, pthread_mutex_t *m,
193    const timespec *abstime) {
194  InitThread();
195  pthread_cond_t *cond = init_cond(c);
196  MutexBeforeUnlock(thr, (uptr)m, true);
197  MutexBeforeLock(thr, (uptr)m, true);
198  int res = REAL(pthread_cond_timedwait)(cond, m, abstime);
199  MutexAfterLock(thr, (uptr)m, true, false);
200  return res;
201}
202
203INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *c) {
204  InitThread();
205  pthread_cond_t *cond = init_cond(c);
206  return REAL(pthread_cond_signal)(cond);
207}
208
209INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *c) {
210  InitThread();
211  pthread_cond_t *cond = init_cond(c);
212  return REAL(pthread_cond_broadcast)(cond);
213}
214
215INTERCEPTOR(int, pthread_cond_destroy, pthread_cond_t *c) {
216  InitThread();
217  pthread_cond_t *cond = init_cond(c);
218  int res = REAL(pthread_cond_destroy)(cond);
219  free(cond);
220  atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
221  return res;
222}
223
224// for symbolizer
225INTERCEPTOR(char*, realpath, const char *path, char *resolved_path) {
226  InitThread();
227  return REAL(realpath)(path, resolved_path);
228}
229
230INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
231  InitThread();
232  return REAL(read)(fd, ptr, count);
233}
234
235INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
236  InitThread();
237  return REAL(pread)(fd, ptr, count, offset);
238}
239
240extern "C" {
241void __dsan_before_mutex_lock(uptr m, int writelock) {
242  if (!InitThread())
243    return;
244  MutexBeforeLock(thr, m, writelock);
245}
246
247void __dsan_after_mutex_lock(uptr m, int writelock, int trylock) {
248  if (!InitThread())
249    return;
250  MutexAfterLock(thr, m, writelock, trylock);
251}
252
253void __dsan_before_mutex_unlock(uptr m, int writelock) {
254  if (!InitThread())
255    return;
256  MutexBeforeUnlock(thr, m, writelock);
257}
258
259void __dsan_mutex_destroy(uptr m) {
260  if (!InitThread())
261    return;
262  // if (m >= g_data_start && m < g_data_end)
263  //   return;
264  MutexDestroy(thr, m);
265}
266}  // extern "C"
267
268namespace __dsan {
269
270static void InitDataSeg() {
271  MemoryMappingLayout proc_maps(true);
272  char name[128];
273  MemoryMappedSegment segment(name, ARRAY_SIZE(name));
274  bool prev_is_data = false;
275  while (proc_maps.Next(&segment)) {
276    bool is_data = segment.offset != 0 && segment.filename[0] != 0;
277    // BSS may get merged with [heap] in /proc/self/maps. This is not very
278    // reliable.
279    bool is_bss = segment.offset == 0 &&
280                  (segment.filename[0] == 0 ||
281                   internal_strcmp(segment.filename, "[heap]") == 0) &&
282                  prev_is_data;
283    if (g_data_start == 0 && is_data) g_data_start = segment.start;
284    if (is_bss) g_data_end = segment.end;
285    prev_is_data = is_data;
286  }
287  VPrintf(1, "guessed data_start=%p data_end=%p\n",  g_data_start, g_data_end);
288  CHECK_LT(g_data_start, g_data_end);
289  CHECK_GE((uptr)&g_data_start, g_data_start);
290  CHECK_LT((uptr)&g_data_start, g_data_end);
291}
292
293void InitializeInterceptors() {
294  INTERCEPT_FUNCTION(pthread_mutex_destroy);
295  INTERCEPT_FUNCTION(pthread_mutex_lock);
296  INTERCEPT_FUNCTION(pthread_mutex_trylock);
297  INTERCEPT_FUNCTION(pthread_mutex_unlock);
298
299  INTERCEPT_FUNCTION(pthread_spin_destroy);
300  INTERCEPT_FUNCTION(pthread_spin_lock);
301  INTERCEPT_FUNCTION(pthread_spin_trylock);
302  INTERCEPT_FUNCTION(pthread_spin_unlock);
303
304  INTERCEPT_FUNCTION(pthread_rwlock_destroy);
305  INTERCEPT_FUNCTION(pthread_rwlock_rdlock);
306  INTERCEPT_FUNCTION(pthread_rwlock_tryrdlock);
307  INTERCEPT_FUNCTION(pthread_rwlock_timedrdlock);
308  INTERCEPT_FUNCTION(pthread_rwlock_wrlock);
309  INTERCEPT_FUNCTION(pthread_rwlock_trywrlock);
310  INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock);
311  INTERCEPT_FUNCTION(pthread_rwlock_unlock);
312
313  INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2");
314  INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2");
315  INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2");
316  INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2");
317  INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
318  INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2");
319
320  // for symbolizer
321  INTERCEPT_FUNCTION(realpath);
322  INTERCEPT_FUNCTION(read);
323  INTERCEPT_FUNCTION(pread);
324
325  InitDataSeg();
326}
327
328}  // namespace __dsan
329