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