1//===-- tsan_test_util_posix.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// This file is a part of ThreadSanitizer (TSan), a race detector.
10//
11// Test utils, Linux, FreeBSD, NetBSD and Darwin implementation.
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_common/sanitizer_atomic.h"
15#include "tsan_interface.h"
16#include "tsan_posix_util.h"
17#include "tsan_rtl.h"
18#include "tsan_test_util.h"
19#include "tsan_report.h"
20
21#include <assert.h>
22#include <pthread.h>
23#include <stdio.h>
24#include <stdint.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28
29#define CALLERPC (__builtin_return_address(0))
30
31static __thread bool expect_report;
32static __thread bool expect_report_reported;
33static __thread __tsan::ReportType expect_report_type;
34
35void ThreadSanitizer::TearDown() {
36 __tsan::ctx->racy_stacks.Reset();
37}
38
39static void *BeforeInitThread(void *param) {
40 (void)param;
41 return 0;
42}
43
44static void AtExit() {
45}
46
47void TestMutexBeforeInit() {
48 // Mutexes must be usable before __tsan_init();
49 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
50 __interceptor_pthread_mutex_lock(mutex: &mtx);
51 __interceptor_pthread_mutex_unlock(mutex: &mtx);
52 __interceptor_pthread_mutex_destroy(mutex: &mtx);
53 pthread_t thr;
54 __interceptor_pthread_create(thread: &thr, attr: 0, start_routine: BeforeInitThread, arg: 0);
55 __interceptor_pthread_join(thread: thr, value_ptr: 0);
56 atexit(AtExit);
57}
58
59namespace __tsan {
60bool OnReport(const ReportDesc *rep, bool suppressed) {
61 if (expect_report) {
62 if (rep->typ != expect_report_type) {
63 printf(format: "Expected report of type %d, got type %d\n",
64 (int)expect_report_type, (int)rep->typ);
65 EXPECT_TRUE(false) << "Wrong report type";
66 return false;
67 }
68 } else {
69 EXPECT_TRUE(false) << "Unexpected report";
70 return false;
71 }
72 expect_report_reported = true;
73 return true;
74}
75} // namespace __tsan
76
77static void* allocate_addr(int size, int offset_from_aligned = 0) {
78 static uintptr_t foo;
79 static __tsan::atomic_uintptr_t uniq = {.val_dont_use: (uintptr_t)&foo}; // Some real address.
80 const int kAlign = 16;
81 CHECK(offset_from_aligned < kAlign);
82 size = (size + 2 * kAlign) & ~(kAlign - 1);
83 uintptr_t addr = atomic_fetch_add(a: &uniq, v: size, mo: __tsan::memory_order_relaxed);
84 return (void*)(addr + offset_from_aligned);
85}
86
87MemLoc::MemLoc(int offset_from_aligned)
88 : loc_(allocate_addr(size: 16, offset_from_aligned)) {
89}
90
91MemLoc::~MemLoc() {
92}
93
94UserMutex::UserMutex(Type type) : alive_(), type_(type) {}
95
96UserMutex::~UserMutex() { CHECK(!alive_); }
97
98void UserMutex::Init() {
99 CHECK(!alive_);
100 alive_ = true;
101 if (type_ == Normal)
102 CHECK_EQ(__interceptor_pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0);
103#ifndef __APPLE__
104 else if (type_ == Spin)
105 CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0);
106#endif
107 else if (type_ == RW)
108 CHECK_EQ(__interceptor_pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0);
109 else
110 CHECK(0);
111}
112
113void UserMutex::StaticInit() {
114 CHECK(!alive_);
115 CHECK(type_ == Normal);
116 alive_ = true;
117 pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER;
118 memcpy(dest: mtx_, src: &tmp, n: sizeof(tmp));
119}
120
121void UserMutex::Destroy() {
122 CHECK(alive_);
123 alive_ = false;
124 if (type_ == Normal)
125 CHECK_EQ(__interceptor_pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0);
126#ifndef __APPLE__
127 else if (type_ == Spin)
128 CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0);
129#endif
130 else if (type_ == RW)
131 CHECK_EQ(__interceptor_pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0);
132}
133
134void UserMutex::Lock() {
135 CHECK(alive_);
136 if (type_ == Normal)
137 CHECK_EQ(__interceptor_pthread_mutex_lock((pthread_mutex_t*)mtx_), 0);
138#ifndef __APPLE__
139 else if (type_ == Spin)
140 CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0);
141#endif
142 else if (type_ == RW)
143 CHECK_EQ(__interceptor_pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0);
144}
145
146bool UserMutex::TryLock() {
147 CHECK(alive_);
148 if (type_ == Normal)
149 return __interceptor_pthread_mutex_trylock(mutex: (pthread_mutex_t*)mtx_) == 0;
150#ifndef __APPLE__
151 else if (type_ == Spin)
152 return pthread_spin_trylock(lock: (pthread_spinlock_t*)mtx_) == 0;
153#endif
154 else if (type_ == RW)
155 return __interceptor_pthread_rwlock_trywrlock(rwlock: (pthread_rwlock_t*)mtx_) == 0;
156 return false;
157}
158
159void UserMutex::Unlock() {
160 CHECK(alive_);
161 if (type_ == Normal)
162 CHECK_EQ(__interceptor_pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0);
163#ifndef __APPLE__
164 else if (type_ == Spin)
165 CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0);
166#endif
167 else if (type_ == RW)
168 CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
169}
170
171void UserMutex::ReadLock() {
172 CHECK(alive_);
173 CHECK(type_ == RW);
174 CHECK_EQ(__interceptor_pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0);
175}
176
177bool UserMutex::TryReadLock() {
178 CHECK(alive_);
179 CHECK(type_ == RW);
180 return __interceptor_pthread_rwlock_tryrdlock(rwlock: (pthread_rwlock_t*)mtx_) == 0;
181}
182
183void UserMutex::ReadUnlock() {
184 CHECK(alive_);
185 CHECK(type_ == RW);
186 CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
187}
188
189struct Event {
190 enum Type {
191 SHUTDOWN,
192 READ,
193 WRITE,
194 VPTR_UPDATE,
195 CALL,
196 RETURN,
197 MUTEX_CREATE,
198 MUTEX_DESTROY,
199 MUTEX_LOCK,
200 MUTEX_TRYLOCK,
201 MUTEX_UNLOCK,
202 MUTEX_READLOCK,
203 MUTEX_TRYREADLOCK,
204 MUTEX_READUNLOCK,
205 MEMCPY,
206 MEMSET
207 };
208 Type type;
209 void *ptr;
210 uptr arg;
211 uptr arg2;
212 bool res;
213 bool expect_report;
214 __tsan::ReportType report_type;
215
216 explicit Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0)
217 : type(type),
218 ptr(const_cast<void *>(ptr)),
219 arg(arg),
220 arg2(arg2),
221 res(),
222 expect_report(),
223 report_type() {}
224
225 void ExpectReport(__tsan::ReportType type) {
226 expect_report = true;
227 report_type = type;
228 }
229};
230
231struct ScopedThread::Impl {
232 pthread_t thread;
233 bool main;
234 bool detached;
235 __tsan::atomic_uintptr_t event; // Event*
236
237 static void *ScopedThreadCallback(void *arg);
238 void send(Event *ev);
239 void HandleEvent(Event *ev);
240};
241
242void ScopedThread::Impl::HandleEvent(Event *ev) {
243 CHECK_EQ(expect_report, false);
244 expect_report = ev->expect_report;
245 expect_report_reported = false;
246 expect_report_type = ev->report_type;
247 switch (ev->type) {
248 case Event::READ:
249 case Event::WRITE: {
250 void (*tsan_mop)(void *addr, void *pc) = 0;
251 if (ev->type == Event::READ) {
252 switch (ev->arg /*size*/) {
253 case 1:
254 tsan_mop = __tsan_read1_pc;
255 break;
256 case 2:
257 tsan_mop = __tsan_read2_pc;
258 break;
259 case 4:
260 tsan_mop = __tsan_read4_pc;
261 break;
262 case 8:
263 tsan_mop = __tsan_read8_pc;
264 break;
265 case 16:
266 tsan_mop = __tsan_read16_pc;
267 break;
268 }
269 } else {
270 switch (ev->arg /*size*/) {
271 case 1:
272 tsan_mop = __tsan_write1_pc;
273 break;
274 case 2:
275 tsan_mop = __tsan_write2_pc;
276 break;
277 case 4:
278 tsan_mop = __tsan_write4_pc;
279 break;
280 case 8:
281 tsan_mop = __tsan_write8_pc;
282 break;
283 case 16:
284 tsan_mop = __tsan_write16_pc;
285 break;
286 }
287 }
288 CHECK_NE(tsan_mop, 0);
289#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__)
290 const int ErrCode = ESOCKTNOSUPPORT;
291#else
292 const int ErrCode = ECHRNG;
293#endif
294 errno = ErrCode;
295 tsan_mop(ev->ptr, (void *)ev->arg2);
296 CHECK_EQ(ErrCode, errno); // In no case must errno be changed.
297 break;
298 }
299 case Event::VPTR_UPDATE:
300 __tsan_vptr_update(vptr_p: (void**)ev->ptr, new_val: (void*)ev->arg);
301 break;
302 case Event::CALL:
303 __tsan_func_entry(call_pc: (void*)((uptr)ev->ptr));
304 break;
305 case Event::RETURN:
306 __tsan_func_exit();
307 break;
308 case Event::MUTEX_CREATE:
309 static_cast<UserMutex *>(ev->ptr)->Init();
310 break;
311 case Event::MUTEX_DESTROY:
312 static_cast<UserMutex *>(ev->ptr)->Destroy();
313 break;
314 case Event::MUTEX_LOCK:
315 static_cast<UserMutex *>(ev->ptr)->Lock();
316 break;
317 case Event::MUTEX_TRYLOCK:
318 ev->res = static_cast<UserMutex *>(ev->ptr)->TryLock();
319 break;
320 case Event::MUTEX_UNLOCK:
321 static_cast<UserMutex *>(ev->ptr)->Unlock();
322 break;
323 case Event::MUTEX_READLOCK:
324 static_cast<UserMutex *>(ev->ptr)->ReadLock();
325 break;
326 case Event::MUTEX_TRYREADLOCK:
327 ev->res = static_cast<UserMutex *>(ev->ptr)->TryReadLock();
328 break;
329 case Event::MUTEX_READUNLOCK:
330 static_cast<UserMutex *>(ev->ptr)->ReadUnlock();
331 break;
332 case Event::MEMCPY:
333 __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2);
334 break;
335 case Event::MEMSET:
336 __interceptor_memset(ev->ptr, ev->arg, ev->arg2);
337 break;
338 default: CHECK(0);
339 }
340 if (expect_report && !expect_report_reported) {
341 printf(format: "Missed expected report of type %d\n", (int)ev->report_type);
342 EXPECT_TRUE(false) << "Missed expected race";
343 }
344 expect_report = false;
345}
346
347void *ScopedThread::Impl::ScopedThreadCallback(void *arg) {
348 __tsan_func_entry(CALLERPC);
349 Impl *impl = (Impl*)arg;
350 for (;;) {
351 Event *ev =
352 (Event *)atomic_load(a: &impl->event, mo: __tsan::memory_order_acquire);
353 if (ev == 0) {
354 sched_yield();
355 continue;
356 }
357 if (ev->type == Event::SHUTDOWN) {
358 atomic_store(a: &impl->event, v: 0, mo: __tsan::memory_order_release);
359 break;
360 }
361 impl->HandleEvent(ev);
362 atomic_store(a: &impl->event, v: 0, mo: __tsan::memory_order_release);
363 }
364 __tsan_func_exit();
365 return 0;
366}
367
368void ScopedThread::Impl::send(Event *e) {
369 if (main) {
370 HandleEvent(ev: e);
371 } else {
372 CHECK_EQ(atomic_load(&event, __tsan::memory_order_relaxed), 0);
373 atomic_store(a: &event, v: (uintptr_t)e, mo: __tsan::memory_order_release);
374 while (atomic_load(a: &event, mo: __tsan::memory_order_acquire) != 0)
375 sched_yield();
376 }
377}
378
379ScopedThread::ScopedThread(bool detached, bool main) {
380 impl_ = new Impl;
381 impl_->main = main;
382 impl_->detached = detached;
383 atomic_store(a: &impl_->event, v: 0, mo: __tsan::memory_order_relaxed);
384 if (!main) {
385 pthread_attr_t attr;
386 pthread_attr_init(attr: &attr);
387 pthread_attr_setdetachstate(
388 attr: &attr, detachstate: detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE);
389 pthread_attr_setstacksize(attr: &attr, stacksize: 64*1024);
390 __interceptor_pthread_create(thread: &impl_->thread, attr: &attr,
391 start_routine: ScopedThread::Impl::ScopedThreadCallback, arg: impl_);
392 }
393}
394
395ScopedThread::~ScopedThread() {
396 if (!impl_->main) {
397 Event event(Event::SHUTDOWN);
398 impl_->send(e: &event);
399 if (!impl_->detached)
400 __interceptor_pthread_join(thread: impl_->thread, value_ptr: 0);
401 }
402 delete impl_;
403}
404
405void ScopedThread::Detach() {
406 CHECK(!impl_->main);
407 CHECK(!impl_->detached);
408 impl_->detached = true;
409 __interceptor_pthread_detach(thread: impl_->thread);
410}
411
412void ScopedThread::Access(void *addr, bool is_write,
413 int size, bool expect_race) {
414 Event event(is_write ? Event::WRITE : Event::READ, addr, size,
415 (uptr)CALLERPC);
416 if (expect_race)
417 event.ExpectReport(type: __tsan::ReportTypeRace);
418 impl_->send(e: &event);
419}
420
421void ScopedThread::VptrUpdate(const MemLoc &vptr,
422 const MemLoc &new_val,
423 bool expect_race) {
424 Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc());
425 if (expect_race)
426 event.ExpectReport(type: __tsan::ReportTypeRace);
427 impl_->send(e: &event);
428}
429
430void ScopedThread::Call(void(*pc)()) {
431 Event event(Event::CALL, (void*)((uintptr_t)pc));
432 impl_->send(e: &event);
433}
434
435void ScopedThread::Return() {
436 Event event(Event::RETURN);
437 impl_->send(e: &event);
438}
439
440void ScopedThread::Create(const UserMutex &m) {
441 Event event(Event::MUTEX_CREATE, &m);
442 impl_->send(e: &event);
443}
444
445void ScopedThread::Destroy(const UserMutex &m) {
446 Event event(Event::MUTEX_DESTROY, &m);
447 impl_->send(e: &event);
448}
449
450void ScopedThread::Lock(const UserMutex &m) {
451 Event event(Event::MUTEX_LOCK, &m);
452 impl_->send(e: &event);
453}
454
455bool ScopedThread::TryLock(const UserMutex &m) {
456 Event event(Event::MUTEX_TRYLOCK, &m);
457 impl_->send(e: &event);
458 return event.res;
459}
460
461void ScopedThread::Unlock(const UserMutex &m) {
462 Event event(Event::MUTEX_UNLOCK, &m);
463 impl_->send(e: &event);
464}
465
466void ScopedThread::ReadLock(const UserMutex &m) {
467 Event event(Event::MUTEX_READLOCK, &m);
468 impl_->send(e: &event);
469}
470
471bool ScopedThread::TryReadLock(const UserMutex &m) {
472 Event event(Event::MUTEX_TRYREADLOCK, &m);
473 impl_->send(e: &event);
474 return event.res;
475}
476
477void ScopedThread::ReadUnlock(const UserMutex &m) {
478 Event event(Event::MUTEX_READUNLOCK, &m);
479 impl_->send(e: &event);
480}
481
482void ScopedThread::Memcpy(void *dst, const void *src, int size,
483 bool expect_race) {
484 Event event(Event::MEMCPY, dst, (uptr)src, size);
485 if (expect_race)
486 event.ExpectReport(type: __tsan::ReportTypeRace);
487 impl_->send(e: &event);
488}
489
490void ScopedThread::Memset(void *dst, int val, int size,
491 bool expect_race) {
492 Event event(Event::MEMSET, dst, val, size);
493 if (expect_race)
494 event.ExpectReport(type: __tsan::ReportTypeRace);
495 impl_->send(e: &event);
496}
497

source code of compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cpp