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