1 | //===-- tsan_rtl.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 | // Main file (entry points) for the TSan run-time. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "tsan_rtl.h" |
15 | |
16 | #include "sanitizer_common/sanitizer_atomic.h" |
17 | #include "sanitizer_common/sanitizer_common.h" |
18 | #include "sanitizer_common/sanitizer_file.h" |
19 | #include "sanitizer_common/sanitizer_interface_internal.h" |
20 | #include "sanitizer_common/sanitizer_libc.h" |
21 | #include "sanitizer_common/sanitizer_placement_new.h" |
22 | #include "sanitizer_common/sanitizer_stackdepot.h" |
23 | #include "sanitizer_common/sanitizer_symbolizer.h" |
24 | #include "tsan_defs.h" |
25 | #include "tsan_interface.h" |
26 | #include "tsan_mman.h" |
27 | #include "tsan_platform.h" |
28 | #include "tsan_suppressions.h" |
29 | #include "tsan_symbolize.h" |
30 | #include "ubsan/ubsan_init.h" |
31 | |
32 | volatile int __tsan_resumed = 0; |
33 | |
34 | extern "C" void __tsan_resume() { |
35 | __tsan_resumed = 1; |
36 | } |
37 | |
38 | #if SANITIZER_APPLE |
39 | SANITIZER_WEAK_DEFAULT_IMPL |
40 | void __tsan_test_only_on_fork() {} |
41 | #endif |
42 | |
43 | namespace __tsan { |
44 | |
45 | #if !SANITIZER_GO |
46 | void (*on_initialize)(void); |
47 | int (*on_finalize)(int); |
48 | #endif |
49 | |
50 | #if !SANITIZER_GO && !SANITIZER_APPLE |
51 | alignas(SANITIZER_CACHE_LINE_SIZE) THREADLOCAL __attribute__((tls_model( |
52 | "initial-exec" ))) char cur_thread_placeholder[sizeof(ThreadState)]; |
53 | #endif |
54 | alignas(SANITIZER_CACHE_LINE_SIZE) static char ctx_placeholder[sizeof(Context)]; |
55 | Context *ctx; |
56 | |
57 | // Can be overriden by a front-end. |
58 | #ifdef TSAN_EXTERNAL_HOOKS |
59 | bool OnFinalize(bool failed); |
60 | void OnInitialize(); |
61 | #else |
62 | SANITIZER_WEAK_CXX_DEFAULT_IMPL |
63 | bool OnFinalize(bool failed) { |
64 | # if !SANITIZER_GO |
65 | if (on_finalize) |
66 | return on_finalize(failed); |
67 | # endif |
68 | return failed; |
69 | } |
70 | |
71 | SANITIZER_WEAK_CXX_DEFAULT_IMPL |
72 | void OnInitialize() { |
73 | # if !SANITIZER_GO |
74 | if (on_initialize) |
75 | on_initialize(); |
76 | # endif |
77 | } |
78 | #endif |
79 | |
80 | static TracePart* TracePartAlloc(ThreadState* thr) { |
81 | TracePart* part = nullptr; |
82 | { |
83 | Lock lock(&ctx->slot_mtx); |
84 | uptr max_parts = Trace::kMinParts + flags()->history_size; |
85 | Trace* trace = &thr->tctx->trace; |
86 | if (trace->parts_allocated == max_parts || |
87 | ctx->trace_part_finished_excess) { |
88 | part = ctx->trace_part_recycle.PopFront(); |
89 | DPrintf("#%d: TracePartAlloc: part=%p\n" , thr->tid, part); |
90 | if (part && part->trace) { |
91 | Trace* trace1 = part->trace; |
92 | Lock trace_lock(&trace1->mtx); |
93 | part->trace = nullptr; |
94 | TracePart* part1 = trace1->parts.PopFront(); |
95 | CHECK_EQ(part, part1); |
96 | if (trace1->parts_allocated > trace1->parts.Size()) { |
97 | ctx->trace_part_finished_excess += |
98 | trace1->parts_allocated - trace1->parts.Size(); |
99 | trace1->parts_allocated = trace1->parts.Size(); |
100 | } |
101 | } |
102 | } |
103 | if (trace->parts_allocated < max_parts) { |
104 | trace->parts_allocated++; |
105 | if (ctx->trace_part_finished_excess) |
106 | ctx->trace_part_finished_excess--; |
107 | } |
108 | if (!part) |
109 | ctx->trace_part_total_allocated++; |
110 | else if (ctx->trace_part_recycle_finished) |
111 | ctx->trace_part_recycle_finished--; |
112 | } |
113 | if (!part) |
114 | part = new (MmapOrDie(size: sizeof(*part), mem_type: "TracePart" )) TracePart(); |
115 | return part; |
116 | } |
117 | |
118 | static void TracePartFree(TracePart* part) SANITIZER_REQUIRES(ctx->slot_mtx) { |
119 | DCHECK(part->trace); |
120 | part->trace = nullptr; |
121 | ctx->trace_part_recycle.PushFront(e: part); |
122 | } |
123 | |
124 | void TraceResetForTesting() { |
125 | Lock lock(&ctx->slot_mtx); |
126 | while (auto* part = ctx->trace_part_recycle.PopFront()) { |
127 | if (auto trace = part->trace) |
128 | CHECK_EQ(trace->parts.PopFront(), part); |
129 | UnmapOrDie(addr: part, size: sizeof(*part)); |
130 | } |
131 | ctx->trace_part_total_allocated = 0; |
132 | ctx->trace_part_recycle_finished = 0; |
133 | ctx->trace_part_finished_excess = 0; |
134 | } |
135 | |
136 | static void DoResetImpl(uptr epoch) { |
137 | ThreadRegistryLock lock0(&ctx->thread_registry); |
138 | Lock lock1(&ctx->slot_mtx); |
139 | CHECK_EQ(ctx->global_epoch, epoch); |
140 | ctx->global_epoch++; |
141 | CHECK(!ctx->resetting); |
142 | ctx->resetting = true; |
143 | for (u32 i = ctx->thread_registry.NumThreadsLocked(); i--;) { |
144 | ThreadContext* tctx = (ThreadContext*)ctx->thread_registry.GetThreadLocked( |
145 | tid: static_cast<Tid>(i)); |
146 | // Potentially we could purge all ThreadStatusDead threads from the |
147 | // registry. Since we reset all shadow, they can't race with anything |
148 | // anymore. However, their tid's can still be stored in some aux places |
149 | // (e.g. tid of thread that created something). |
150 | auto trace = &tctx->trace; |
151 | Lock lock(&trace->mtx); |
152 | bool attached = tctx->thr && tctx->thr->slot; |
153 | auto parts = &trace->parts; |
154 | bool local = false; |
155 | while (!parts->Empty()) { |
156 | auto part = parts->Front(); |
157 | local = local || part == trace->local_head; |
158 | if (local) |
159 | CHECK(!ctx->trace_part_recycle.Queued(part)); |
160 | else |
161 | ctx->trace_part_recycle.Remove(e: part); |
162 | if (attached && parts->Size() == 1) { |
163 | // The thread is running and this is the last/current part. |
164 | // Set the trace position to the end of the current part |
165 | // to force the thread to call SwitchTracePart and re-attach |
166 | // to a new slot and allocate a new trace part. |
167 | // Note: the thread is concurrently modifying the position as well, |
168 | // so this is only best-effort. The thread can only modify position |
169 | // within this part, because switching parts is protected by |
170 | // slot/trace mutexes that we hold here. |
171 | atomic_store_relaxed( |
172 | a: &tctx->thr->trace_pos, |
173 | v: reinterpret_cast<uptr>(&part->events[TracePart::kSize])); |
174 | break; |
175 | } |
176 | parts->Remove(e: part); |
177 | TracePartFree(part); |
178 | } |
179 | CHECK_LE(parts->Size(), 1); |
180 | trace->local_head = parts->Front(); |
181 | if (tctx->thr && !tctx->thr->slot) { |
182 | atomic_store_relaxed(a: &tctx->thr->trace_pos, v: 0); |
183 | tctx->thr->trace_prev_pc = 0; |
184 | } |
185 | if (trace->parts_allocated > trace->parts.Size()) { |
186 | ctx->trace_part_finished_excess += |
187 | trace->parts_allocated - trace->parts.Size(); |
188 | trace->parts_allocated = trace->parts.Size(); |
189 | } |
190 | } |
191 | while (ctx->slot_queue.PopFront()) { |
192 | } |
193 | for (auto& slot : ctx->slots) { |
194 | slot.SetEpoch(kEpochZero); |
195 | slot.journal.Reset(); |
196 | slot.thr = nullptr; |
197 | ctx->slot_queue.PushBack(e: &slot); |
198 | } |
199 | |
200 | DPrintf("Resetting shadow...\n" ); |
201 | auto shadow_begin = ShadowBeg(); |
202 | auto shadow_end = ShadowEnd(); |
203 | #if SANITIZER_GO |
204 | CHECK_NE(0, ctx->mapped_shadow_begin); |
205 | shadow_begin = ctx->mapped_shadow_begin; |
206 | shadow_end = ctx->mapped_shadow_end; |
207 | VPrintf(2, "shadow_begin-shadow_end: (0x%zx-0x%zx)\n" , |
208 | shadow_begin, shadow_end); |
209 | #endif |
210 | |
211 | #if SANITIZER_WINDOWS |
212 | auto resetFailed = |
213 | !ZeroMmapFixedRegion(shadow_begin, shadow_end - shadow_begin); |
214 | #else |
215 | auto resetFailed = |
216 | !MmapFixedSuperNoReserve(fixed_addr: shadow_begin, size: shadow_end-shadow_begin, name: "shadow" ); |
217 | # if !SANITIZER_GO |
218 | DontDumpShadow(addr: shadow_begin, size: shadow_end - shadow_begin); |
219 | # endif |
220 | #endif |
221 | if (resetFailed) { |
222 | Printf(format: "failed to reset shadow memory\n" ); |
223 | Die(); |
224 | } |
225 | DPrintf("Resetting meta shadow...\n" ); |
226 | ctx->metamap.ResetClocks(); |
227 | StoreShadow(sp: &ctx->last_spurious_race, s: Shadow::kEmpty); |
228 | ctx->resetting = false; |
229 | } |
230 | |
231 | // Clang does not understand locking all slots in the loop: |
232 | // error: expecting mutex 'slot.mtx' to be held at start of each loop |
233 | void DoReset(ThreadState* thr, uptr epoch) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { |
234 | for (auto& slot : ctx->slots) { |
235 | slot.mtx.Lock(); |
236 | if (UNLIKELY(epoch == 0)) |
237 | epoch = ctx->global_epoch; |
238 | if (UNLIKELY(epoch != ctx->global_epoch)) { |
239 | // Epoch can't change once we've locked the first slot. |
240 | CHECK_EQ(slot.sid, 0); |
241 | slot.mtx.Unlock(); |
242 | return; |
243 | } |
244 | } |
245 | DPrintf("#%d: DoReset epoch=%lu\n" , thr ? thr->tid : -1, epoch); |
246 | DoResetImpl(epoch); |
247 | for (auto& slot : ctx->slots) slot.mtx.Unlock(); |
248 | } |
249 | |
250 | void FlushShadowMemory() { DoReset(thr: nullptr, epoch: 0); } |
251 | |
252 | static TidSlot* FindSlotAndLock(ThreadState* thr) |
253 | SANITIZER_ACQUIRE(thr->slot->mtx) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { |
254 | CHECK(!thr->slot); |
255 | TidSlot* slot = nullptr; |
256 | for (;;) { |
257 | uptr epoch; |
258 | { |
259 | Lock lock(&ctx->slot_mtx); |
260 | epoch = ctx->global_epoch; |
261 | if (slot) { |
262 | // This is an exhausted slot from the previous iteration. |
263 | if (ctx->slot_queue.Queued(e: slot)) |
264 | ctx->slot_queue.Remove(e: slot); |
265 | thr->slot_locked = false; |
266 | slot->mtx.Unlock(); |
267 | } |
268 | for (;;) { |
269 | slot = ctx->slot_queue.PopFront(); |
270 | if (!slot) |
271 | break; |
272 | if (slot->epoch() != kEpochLast) { |
273 | ctx->slot_queue.PushBack(e: slot); |
274 | break; |
275 | } |
276 | } |
277 | } |
278 | if (!slot) { |
279 | DoReset(thr, epoch); |
280 | continue; |
281 | } |
282 | slot->mtx.Lock(); |
283 | CHECK(!thr->slot_locked); |
284 | thr->slot_locked = true; |
285 | if (slot->thr) { |
286 | DPrintf("#%d: preempting sid=%d tid=%d\n" , thr->tid, (u32)slot->sid, |
287 | slot->thr->tid); |
288 | slot->SetEpoch(slot->thr->fast_state.epoch()); |
289 | slot->thr = nullptr; |
290 | } |
291 | if (slot->epoch() != kEpochLast) |
292 | return slot; |
293 | } |
294 | } |
295 | |
296 | void SlotAttachAndLock(ThreadState* thr) { |
297 | TidSlot* slot = FindSlotAndLock(thr); |
298 | DPrintf("#%d: SlotAttach: slot=%u\n" , thr->tid, static_cast<int>(slot->sid)); |
299 | CHECK(!slot->thr); |
300 | CHECK(!thr->slot); |
301 | slot->thr = thr; |
302 | thr->slot = slot; |
303 | Epoch epoch = EpochInc(epoch: slot->epoch()); |
304 | CHECK(!EpochOverflow(epoch)); |
305 | slot->SetEpoch(epoch); |
306 | thr->fast_state.SetSid(slot->sid); |
307 | thr->fast_state.SetEpoch(epoch); |
308 | if (thr->slot_epoch != ctx->global_epoch) { |
309 | thr->slot_epoch = ctx->global_epoch; |
310 | thr->clock.Reset(); |
311 | #if !SANITIZER_GO |
312 | thr->last_sleep_stack_id = kInvalidStackID; |
313 | thr->last_sleep_clock.Reset(); |
314 | #endif |
315 | } |
316 | thr->clock.Set(sid: slot->sid, v: epoch); |
317 | slot->journal.PushBack(v: {.tid: thr->tid, .epoch: epoch}); |
318 | } |
319 | |
320 | static void SlotDetachImpl(ThreadState* thr, bool exiting) { |
321 | TidSlot* slot = thr->slot; |
322 | thr->slot = nullptr; |
323 | if (thr != slot->thr) { |
324 | slot = nullptr; // we don't own the slot anymore |
325 | if (thr->slot_epoch != ctx->global_epoch) { |
326 | TracePart* part = nullptr; |
327 | auto* trace = &thr->tctx->trace; |
328 | { |
329 | Lock l(&trace->mtx); |
330 | auto* parts = &trace->parts; |
331 | // The trace can be completely empty in an unlikely event |
332 | // the thread is preempted right after it acquired the slot |
333 | // in ThreadStart and did not trace any events yet. |
334 | CHECK_LE(parts->Size(), 1); |
335 | part = parts->PopFront(); |
336 | thr->tctx->trace.local_head = nullptr; |
337 | atomic_store_relaxed(a: &thr->trace_pos, v: 0); |
338 | thr->trace_prev_pc = 0; |
339 | } |
340 | if (part) { |
341 | Lock l(&ctx->slot_mtx); |
342 | TracePartFree(part); |
343 | } |
344 | } |
345 | return; |
346 | } |
347 | CHECK(exiting || thr->fast_state.epoch() == kEpochLast); |
348 | slot->SetEpoch(thr->fast_state.epoch()); |
349 | slot->thr = nullptr; |
350 | } |
351 | |
352 | void SlotDetach(ThreadState* thr) { |
353 | Lock lock(&thr->slot->mtx); |
354 | SlotDetachImpl(thr, exiting: true); |
355 | } |
356 | |
357 | void SlotLock(ThreadState* thr) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { |
358 | DCHECK(!thr->slot_locked); |
359 | #if SANITIZER_DEBUG |
360 | // Check these mutexes are not locked. |
361 | // We can call DoReset from SlotAttachAndLock, which will lock |
362 | // these mutexes, but it happens only every once in a while. |
363 | { ThreadRegistryLock lock(&ctx->thread_registry); } |
364 | { Lock lock(&ctx->slot_mtx); } |
365 | #endif |
366 | TidSlot* slot = thr->slot; |
367 | slot->mtx.Lock(); |
368 | thr->slot_locked = true; |
369 | if (LIKELY(thr == slot->thr && thr->fast_state.epoch() != kEpochLast)) |
370 | return; |
371 | SlotDetachImpl(thr, exiting: false); |
372 | thr->slot_locked = false; |
373 | slot->mtx.Unlock(); |
374 | SlotAttachAndLock(thr); |
375 | } |
376 | |
377 | void SlotUnlock(ThreadState* thr) { |
378 | DCHECK(thr->slot_locked); |
379 | thr->slot_locked = false; |
380 | thr->slot->mtx.Unlock(); |
381 | } |
382 | |
383 | Context::Context() |
384 | : initialized(), |
385 | report_mtx(MutexTypeReport), |
386 | nreported(), |
387 | thread_registry([](Tid tid) -> ThreadContextBase* { |
388 | return new (Alloc(sz: sizeof(ThreadContext))) ThreadContext(tid); |
389 | }), |
390 | racy_mtx(MutexTypeRacy), |
391 | racy_stacks(), |
392 | fired_suppressions_mtx(MutexTypeFired), |
393 | slot_mtx(MutexTypeSlots), |
394 | resetting() { |
395 | fired_suppressions.reserve(new_size: 8); |
396 | for (uptr i = 0; i < ARRAY_SIZE(slots); i++) { |
397 | TidSlot* slot = &slots[i]; |
398 | slot->sid = static_cast<Sid>(i); |
399 | slot_queue.PushBack(e: slot); |
400 | } |
401 | global_epoch = 1; |
402 | } |
403 | |
404 | TidSlot::TidSlot() : mtx(MutexTypeSlot) {} |
405 | |
406 | // The objects are allocated in TLS, so one may rely on zero-initialization. |
407 | ThreadState::ThreadState(Tid tid) |
408 | // Do not touch these, rely on zero initialization, |
409 | // they may be accessed before the ctor. |
410 | // ignore_reads_and_writes() |
411 | // ignore_interceptors() |
412 | : tid(tid) { |
413 | CHECK_EQ(reinterpret_cast<uptr>(this) % SANITIZER_CACHE_LINE_SIZE, 0); |
414 | #if !SANITIZER_GO |
415 | // C/C++ uses fixed size shadow stack. |
416 | const int kInitStackSize = kShadowStackSize; |
417 | shadow_stack = static_cast<uptr*>( |
418 | MmapNoReserveOrDie(size: kInitStackSize * sizeof(uptr), mem_type: "shadow stack" )); |
419 | SetShadowRegionHugePageMode(addr: reinterpret_cast<uptr>(shadow_stack), |
420 | length: kInitStackSize * sizeof(uptr)); |
421 | #else |
422 | // Go uses malloc-allocated shadow stack with dynamic size. |
423 | const int kInitStackSize = 8; |
424 | shadow_stack = static_cast<uptr*>(Alloc(kInitStackSize * sizeof(uptr))); |
425 | #endif |
426 | shadow_stack_pos = shadow_stack; |
427 | shadow_stack_end = shadow_stack + kInitStackSize; |
428 | } |
429 | |
430 | #if !SANITIZER_GO |
431 | void MemoryProfiler(u64 uptime) { |
432 | if (ctx->memprof_fd == kInvalidFd) |
433 | return; |
434 | InternalMmapVector<char> buf(4096); |
435 | WriteMemoryProfile(buf: buf.data(), buf_size: buf.size(), uptime_ns: uptime); |
436 | WriteToFile(fd: ctx->memprof_fd, buff: buf.data(), buff_size: internal_strlen(s: buf.data())); |
437 | } |
438 | |
439 | static bool InitializeMemoryProfiler() { |
440 | ctx->memprof_fd = kInvalidFd; |
441 | const char *fname = flags()->profile_memory; |
442 | if (!fname || !fname[0]) |
443 | return false; |
444 | if (internal_strcmp(s1: fname, s2: "stdout" ) == 0) { |
445 | ctx->memprof_fd = 1; |
446 | } else if (internal_strcmp(s1: fname, s2: "stderr" ) == 0) { |
447 | ctx->memprof_fd = 2; |
448 | } else { |
449 | InternalScopedString filename; |
450 | filename.AppendF(format: "%s.%d" , fname, (int)internal_getpid()); |
451 | ctx->memprof_fd = OpenFile(filename: filename.data(), mode: WrOnly); |
452 | if (ctx->memprof_fd == kInvalidFd) { |
453 | Printf(format: "ThreadSanitizer: failed to open memory profile file '%s'\n" , |
454 | filename.data()); |
455 | return false; |
456 | } |
457 | } |
458 | MemoryProfiler(uptime: 0); |
459 | return true; |
460 | } |
461 | |
462 | static void *BackgroundThread(void *arg) { |
463 | // This is a non-initialized non-user thread, nothing to see here. |
464 | // We don't use ScopedIgnoreInterceptors, because we want ignores to be |
465 | // enabled even when the thread function exits (e.g. during pthread thread |
466 | // shutdown code). |
467 | cur_thread_init()->ignore_interceptors++; |
468 | const u64 kMs2Ns = 1000 * 1000; |
469 | const u64 start = NanoTime(); |
470 | |
471 | u64 last_flush = start; |
472 | uptr = 0; |
473 | while (!atomic_load_relaxed(a: &ctx->stop_background_thread)) { |
474 | SleepForMillis(millis: 100); |
475 | u64 now = NanoTime(); |
476 | |
477 | // Flush memory if requested. |
478 | if (flags()->flush_memory_ms > 0) { |
479 | if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { |
480 | VReport(1, "ThreadSanitizer: periodic memory flush\n" ); |
481 | FlushShadowMemory(); |
482 | now = last_flush = NanoTime(); |
483 | } |
484 | } |
485 | if (flags()->memory_limit_mb > 0) { |
486 | uptr = GetRSS(); |
487 | uptr limit = uptr(flags()->memory_limit_mb) << 20; |
488 | VReport(1, |
489 | "ThreadSanitizer: memory flush check" |
490 | " RSS=%llu LAST=%llu LIMIT=%llu\n" , |
491 | (u64)rss >> 20, (u64)last_rss >> 20, (u64)limit >> 20); |
492 | if (2 * rss > limit + last_rss) { |
493 | VReport(1, "ThreadSanitizer: flushing memory due to RSS\n" ); |
494 | FlushShadowMemory(); |
495 | rss = GetRSS(); |
496 | now = NanoTime(); |
497 | VReport(1, "ThreadSanitizer: memory flushed RSS=%llu\n" , |
498 | (u64)rss >> 20); |
499 | } |
500 | last_rss = rss; |
501 | } |
502 | |
503 | MemoryProfiler(uptime: now - start); |
504 | |
505 | // Flush symbolizer cache if requested. |
506 | if (flags()->flush_symbolizer_ms > 0) { |
507 | u64 last = atomic_load(a: &ctx->last_symbolize_time_ns, |
508 | mo: memory_order_relaxed); |
509 | if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) { |
510 | Lock l(&ctx->report_mtx); |
511 | ScopedErrorReportLock l2; |
512 | SymbolizeFlush(); |
513 | atomic_store(a: &ctx->last_symbolize_time_ns, v: 0, mo: memory_order_relaxed); |
514 | } |
515 | } |
516 | } |
517 | return nullptr; |
518 | } |
519 | |
520 | static void StartBackgroundThread() { |
521 | ctx->background_thread = internal_start_thread(func: &BackgroundThread, arg: 0); |
522 | } |
523 | |
524 | #ifndef __mips__ |
525 | static void StopBackgroundThread() { |
526 | atomic_store(a: &ctx->stop_background_thread, v: 1, mo: memory_order_relaxed); |
527 | internal_join_thread(th: ctx->background_thread); |
528 | ctx->background_thread = 0; |
529 | } |
530 | #endif |
531 | #endif |
532 | |
533 | void DontNeedShadowFor(uptr addr, uptr size) { |
534 | ReleaseMemoryPagesToOS(beg: reinterpret_cast<uptr>(MemToShadow(x: addr)), |
535 | end: reinterpret_cast<uptr>(MemToShadow(x: addr + size))); |
536 | } |
537 | |
538 | #if !SANITIZER_GO |
539 | // We call UnmapShadow before the actual munmap, at that point we don't yet |
540 | // know if the provided address/size are sane. We can't call UnmapShadow |
541 | // after the actual munmap becuase at that point the memory range can |
542 | // already be reused for something else, so we can't rely on the munmap |
543 | // return value to understand is the values are sane. |
544 | // While calling munmap with insane values (non-canonical address, negative |
545 | // size, etc) is an error, the kernel won't crash. We must also try to not |
546 | // crash as the failure mode is very confusing (paging fault inside of the |
547 | // runtime on some derived shadow address). |
548 | static bool IsValidMmapRange(uptr addr, uptr size) { |
549 | if (size == 0) |
550 | return true; |
551 | if (static_cast<sptr>(size) < 0) |
552 | return false; |
553 | if (!IsAppMem(mem: addr) || !IsAppMem(mem: addr + size - 1)) |
554 | return false; |
555 | // Check that if the start of the region belongs to one of app ranges, |
556 | // end of the region belongs to the same region. |
557 | const uptr ranges[][2] = { |
558 | {LoAppMemBeg(), LoAppMemEnd()}, |
559 | {MidAppMemBeg(), MidAppMemEnd()}, |
560 | {HiAppMemBeg(), HiAppMemEnd()}, |
561 | }; |
562 | for (auto range : ranges) { |
563 | if (addr >= range[0] && addr < range[1]) |
564 | return addr + size <= range[1]; |
565 | } |
566 | return false; |
567 | } |
568 | |
569 | void UnmapShadow(ThreadState* thr, uptr addr, uptr size) { |
570 | if (size == 0 || !IsValidMmapRange(addr, size)) |
571 | return; |
572 | // unmap shadow is related to semantic of mmap/munmap, so we |
573 | // should clear the whole shadow range, including the tail shadow |
574 | // while addr + size % kShadowCell != 0. |
575 | uptr rounded_size_shadow = RoundUp(p: addr + size, align: kShadowCell) - addr; |
576 | DontNeedShadowFor(addr, size: rounded_size_shadow); |
577 | ScopedGlobalProcessor sgp; |
578 | SlotLocker locker(thr, true); |
579 | uptr rounded_size_meta = RoundUp(p: addr + size, align: kMetaShadowCell) - addr; |
580 | ctx->metamap.ResetRange(proc: thr->proc(), p: addr, sz: rounded_size_meta, reset: true); |
581 | } |
582 | #endif |
583 | |
584 | void MapShadow(uptr addr, uptr size) { |
585 | // Although named MapShadow, this function's semantic is unrelated to |
586 | // UnmapShadow. This function currently only used for Go's lazy allocation |
587 | // of shadow, whose targets are program section (e.g., bss, data, etc.). |
588 | // Therefore, we can guarantee that the addr and size align to kShadowCell |
589 | // and kMetaShadowCell by the following assertions. |
590 | DCHECK_EQ(addr % kShadowCell, 0); |
591 | DCHECK_EQ(size % kShadowCell, 0); |
592 | DCHECK_EQ(addr % kMetaShadowCell, 0); |
593 | DCHECK_EQ(size % kMetaShadowCell, 0); |
594 | |
595 | // Ensure thead registry lock held, so as to synchronize |
596 | // with DoReset, which also access the mapped_shadow_* ctxt fields. |
597 | ThreadRegistryLock lock0(&ctx->thread_registry); |
598 | static bool data_mapped = false; |
599 | |
600 | #if !SANITIZER_GO |
601 | // Global data is not 64K aligned, but there are no adjacent mappings, |
602 | // so we can get away with unaligned mapping. |
603 | // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment |
604 | const uptr kPageSize = GetPageSizeCached(); |
605 | uptr shadow_begin = RoundDownTo(x: (uptr)MemToShadow(x: addr), boundary: kPageSize); |
606 | uptr shadow_end = RoundUpTo(size: (uptr)MemToShadow(x: addr + size), boundary: kPageSize); |
607 | if (!MmapFixedNoReserve(fixed_addr: shadow_begin, size: shadow_end - shadow_begin, name: "shadow" )) |
608 | Die(); |
609 | #else |
610 | uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), (64 << 10)); |
611 | uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), (64 << 10)); |
612 | VPrintf(2, "MapShadow for (0x%zx-0x%zx), begin/end: (0x%zx-0x%zx)\n" , |
613 | addr, addr + size, shadow_begin, shadow_end); |
614 | |
615 | if (!data_mapped) { |
616 | // First call maps data+bss. |
617 | if (!MmapFixedSuperNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow" )) |
618 | Die(); |
619 | } else { |
620 | VPrintf(2, "ctx->mapped_shadow_{begin,end} = (0x%zx-0x%zx)\n" , |
621 | ctx->mapped_shadow_begin, ctx->mapped_shadow_end); |
622 | // Second and subsequent calls map heap. |
623 | if (shadow_end <= ctx->mapped_shadow_end) |
624 | return; |
625 | if (!ctx->mapped_shadow_begin || ctx->mapped_shadow_begin > shadow_begin) |
626 | ctx->mapped_shadow_begin = shadow_begin; |
627 | if (shadow_begin < ctx->mapped_shadow_end) |
628 | shadow_begin = ctx->mapped_shadow_end; |
629 | VPrintf(2, "MapShadow begin/end = (0x%zx-0x%zx)\n" , |
630 | shadow_begin, shadow_end); |
631 | if (!MmapFixedSuperNoReserve(shadow_begin, shadow_end - shadow_begin, |
632 | "shadow" )) |
633 | Die(); |
634 | ctx->mapped_shadow_end = shadow_end; |
635 | } |
636 | #endif |
637 | |
638 | // Meta shadow is 2:1, so tread carefully. |
639 | static uptr mapped_meta_end = 0; |
640 | uptr meta_begin = (uptr)MemToMeta(x: addr); |
641 | uptr meta_end = (uptr)MemToMeta(x: addr + size); |
642 | // Windows wants 64K alignment. |
643 | meta_begin = RoundDownTo(x: meta_begin, boundary: 64 << 10); |
644 | meta_end = RoundUpTo(size: meta_end, boundary: 64 << 10); |
645 | if (!data_mapped) { |
646 | // First call maps data+bss. |
647 | data_mapped = true; |
648 | if (!MmapFixedSuperNoReserve(fixed_addr: meta_begin, size: meta_end - meta_begin, |
649 | name: "meta shadow" )) |
650 | Die(); |
651 | } else { |
652 | // Mapping continuous heap. |
653 | CHECK_GT(meta_end, mapped_meta_end); |
654 | if (meta_begin < mapped_meta_end) |
655 | meta_begin = mapped_meta_end; |
656 | if (!MmapFixedSuperNoReserve(fixed_addr: meta_begin, size: meta_end - meta_begin, |
657 | name: "meta shadow" )) |
658 | Die(); |
659 | mapped_meta_end = meta_end; |
660 | } |
661 | VPrintf(2, "mapped meta shadow for (0x%zx-0x%zx) at (0x%zx-0x%zx)\n" , addr, |
662 | addr + size, meta_begin, meta_end); |
663 | } |
664 | |
665 | #if !SANITIZER_GO |
666 | static void OnStackUnwind(const SignalContext &sig, const void *, |
667 | BufferedStackTrace *stack) { |
668 | stack->Unwind(pc: StackTrace::GetNextInstructionPc(pc: sig.pc), bp: sig.bp, context: sig.context, |
669 | request_fast: common_flags()->fast_unwind_on_fatal); |
670 | } |
671 | |
672 | static void TsanOnDeadlySignal(int signo, void *siginfo, void *context) { |
673 | HandleDeadlySignal(siginfo, context, tid: GetTid(), unwind: &OnStackUnwind, unwind_context: nullptr); |
674 | } |
675 | #endif |
676 | |
677 | void CheckUnwind() { |
678 | // There is high probability that interceptors will check-fail as well, |
679 | // on the other hand there is no sense in processing interceptors |
680 | // since we are going to die soon. |
681 | ScopedIgnoreInterceptors ignore; |
682 | #if !SANITIZER_GO |
683 | ThreadState* thr = cur_thread(); |
684 | thr->nomalloc = false; |
685 | thr->ignore_sync++; |
686 | thr->ignore_reads_and_writes++; |
687 | atomic_store_relaxed(a: &thr->in_signal_handler, v: 0); |
688 | #endif |
689 | PrintCurrentStack(pc: StackTrace::GetCurrentPc(), |
690 | fast: common_flags()->fast_unwind_on_fatal); |
691 | } |
692 | |
693 | bool is_initialized; |
694 | |
695 | // Symbolization indirectly calls dl_iterate_phdr. If a CHECK() fails early on |
696 | // (prior to the dl_iterate_phdr interceptor setup), resulting in an attempted |
697 | // symbolization, it will segfault. |
698 | // dl_iterate_phdr is not intercepted for Android. |
699 | bool ready_to_symbolize = SANITIZER_ANDROID; |
700 | |
701 | void Initialize(ThreadState *thr) { |
702 | // Thread safe because done before all threads exist. |
703 | if (is_initialized) |
704 | return; |
705 | is_initialized = true; |
706 | // We are not ready to handle interceptors yet. |
707 | ScopedIgnoreInterceptors ignore; |
708 | SanitizerToolName = "ThreadSanitizer" ; |
709 | // Install tool-specific callbacks in sanitizer_common. |
710 | SetCheckUnwindCallback(CheckUnwind); |
711 | |
712 | ctx = new(ctx_placeholder) Context; |
713 | const char *env_name = SANITIZER_GO ? "GORACE" : "TSAN_OPTIONS" ; |
714 | const char *options = GetEnv(name: env_name); |
715 | CacheBinaryName(); |
716 | CheckASLR(); |
717 | InitializeFlags(flags: &ctx->flags, env: options, env_option_name: env_name); |
718 | AvoidCVE_2016_2143(); |
719 | __sanitizer::InitializePlatformEarly(); |
720 | __tsan::InitializePlatformEarly(); |
721 | |
722 | #if !SANITIZER_GO |
723 | InitializeAllocator(); |
724 | ReplaceSystemMalloc(); |
725 | #endif |
726 | if (common_flags()->detect_deadlocks) |
727 | ctx->dd = DDetector::Create(flags: flags()); |
728 | Processor *proc = ProcCreate(); |
729 | ProcWire(proc, thr); |
730 | InitializeInterceptors(); |
731 | InitializePlatform(); |
732 | InitializeDynamicAnnotations(); |
733 | #if !SANITIZER_GO |
734 | InitializeShadowMemory(); |
735 | InitializeAllocatorLate(); |
736 | InstallDeadlySignalHandlers(handler: TsanOnDeadlySignal); |
737 | #endif |
738 | // Setup correct file descriptor for error reports. |
739 | __sanitizer_set_report_path(path: common_flags()->log_path); |
740 | InitializeSuppressions(); |
741 | #if !SANITIZER_GO |
742 | InitializeLibIgnore(); |
743 | Symbolizer::GetOrInit()->AddHooks(start_hook: EnterSymbolizer, end_hook: ExitSymbolizer); |
744 | #endif |
745 | |
746 | VPrintf(1, "***** Running under ThreadSanitizer v3 (pid %d) *****\n" , |
747 | (int)internal_getpid()); |
748 | |
749 | // Initialize thread 0. |
750 | Tid tid = ThreadCreate(thr: nullptr, pc: 0, uid: 0, detached: true); |
751 | CHECK_EQ(tid, kMainTid); |
752 | ThreadStart(thr, tid, os_id: GetTid(), thread_type: ThreadType::Regular); |
753 | #if TSAN_CONTAINS_UBSAN |
754 | __ubsan::InitAsPlugin(); |
755 | #endif |
756 | |
757 | #if !SANITIZER_GO |
758 | Symbolizer::LateInitialize(); |
759 | if (InitializeMemoryProfiler() || flags()->force_background_thread) |
760 | MaybeSpawnBackgroundThread(); |
761 | #endif |
762 | ctx->initialized = true; |
763 | |
764 | if (flags()->stop_on_start) { |
765 | Printf(format: "ThreadSanitizer is suspended at startup (pid %d)." |
766 | " Call __tsan_resume().\n" , |
767 | (int)internal_getpid()); |
768 | while (__tsan_resumed == 0) {} |
769 | } |
770 | |
771 | OnInitialize(); |
772 | } |
773 | |
774 | void MaybeSpawnBackgroundThread() { |
775 | // On MIPS, TSan initialization is run before |
776 | // __pthread_initialize_minimal_internal() is finished, so we can not spawn |
777 | // new threads. |
778 | #if !SANITIZER_GO && !defined(__mips__) |
779 | static atomic_uint32_t bg_thread = {}; |
780 | if (atomic_load(a: &bg_thread, mo: memory_order_relaxed) == 0 && |
781 | atomic_exchange(a: &bg_thread, v: 1, mo: memory_order_relaxed) == 0) { |
782 | StartBackgroundThread(); |
783 | SetSandboxingCallback(StopBackgroundThread); |
784 | } |
785 | #endif |
786 | } |
787 | |
788 | int Finalize(ThreadState *thr) { |
789 | bool failed = false; |
790 | |
791 | #if !SANITIZER_GO |
792 | if (common_flags()->print_module_map == 1) |
793 | DumpProcessMap(); |
794 | #endif |
795 | |
796 | if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1) |
797 | internal_usleep(useconds: u64(flags()->atexit_sleep_ms) * 1000); |
798 | |
799 | { |
800 | // Wait for pending reports. |
801 | ScopedErrorReportLock lock; |
802 | } |
803 | |
804 | #if !SANITIZER_GO |
805 | if (Verbosity()) AllocatorPrintStats(); |
806 | #endif |
807 | |
808 | ThreadFinalize(thr); |
809 | |
810 | if (ctx->nreported) { |
811 | failed = true; |
812 | #if !SANITIZER_GO |
813 | Printf(format: "ThreadSanitizer: reported %d warnings\n" , ctx->nreported); |
814 | #else |
815 | Printf("Found %d data race(s)\n" , ctx->nreported); |
816 | #endif |
817 | } |
818 | |
819 | if (common_flags()->print_suppressions) |
820 | PrintMatchedSuppressions(); |
821 | |
822 | failed = OnFinalize(failed); |
823 | |
824 | return failed ? common_flags()->exitcode : 0; |
825 | } |
826 | |
827 | #if !SANITIZER_GO |
828 | void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { |
829 | VReport(2, "BeforeFork tid: %llu\n" , GetTid()); |
830 | GlobalProcessorLock(); |
831 | // Detaching from the slot makes OnUserFree skip writing to the shadow. |
832 | // The slot will be locked so any attempts to use it will deadlock anyway. |
833 | SlotDetach(thr); |
834 | for (auto& slot : ctx->slots) slot.mtx.Lock(); |
835 | ctx->thread_registry.Lock(); |
836 | ctx->slot_mtx.Lock(); |
837 | ScopedErrorReportLock::Lock(); |
838 | AllocatorLockBeforeFork(); |
839 | // Suppress all reports in the pthread_atfork callbacks. |
840 | // Reports will deadlock on the report_mtx. |
841 | // We could ignore sync operations as well, |
842 | // but so far it's unclear if it will do more good or harm. |
843 | // Unnecessarily ignoring things can lead to false positives later. |
844 | thr->suppress_reports++; |
845 | // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and |
846 | // we'll assert in CheckNoLocks() unless we ignore interceptors. |
847 | // On OS X libSystem_atfork_prepare/parent/child callbacks are called |
848 | // after/before our callbacks and they call free. |
849 | thr->ignore_interceptors++; |
850 | // Disables memory write in OnUserAlloc/Free. |
851 | thr->ignore_reads_and_writes++; |
852 | |
853 | # if SANITIZER_APPLE |
854 | __tsan_test_only_on_fork(); |
855 | # endif |
856 | } |
857 | |
858 | static void ForkAfter(ThreadState* thr, |
859 | bool child) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { |
860 | thr->suppress_reports--; // Enabled in ForkBefore. |
861 | thr->ignore_interceptors--; |
862 | thr->ignore_reads_and_writes--; |
863 | AllocatorUnlockAfterFork(child); |
864 | ScopedErrorReportLock::Unlock(); |
865 | ctx->slot_mtx.Unlock(); |
866 | ctx->thread_registry.Unlock(); |
867 | for (auto& slot : ctx->slots) slot.mtx.Unlock(); |
868 | SlotAttachAndLock(thr); |
869 | SlotUnlock(thr); |
870 | GlobalProcessorUnlock(); |
871 | VReport(2, "AfterFork tid: %llu\n" , GetTid()); |
872 | } |
873 | |
874 | void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr, child: false); } |
875 | |
876 | void ForkChildAfter(ThreadState* thr, uptr pc, bool start_thread) { |
877 | ForkAfter(thr, child: true); |
878 | u32 nthread = ctx->thread_registry.OnFork(tid: thr->tid); |
879 | VPrintf(1, |
880 | "ThreadSanitizer: forked new process with pid %d," |
881 | " parent had %d threads\n" , |
882 | (int)internal_getpid(), (int)nthread); |
883 | if (nthread == 1) { |
884 | if (start_thread) |
885 | StartBackgroundThread(); |
886 | } else { |
887 | // We've just forked a multi-threaded process. We cannot reasonably function |
888 | // after that (some mutexes may be locked before fork). So just enable |
889 | // ignores for everything in the hope that we will exec soon. |
890 | ctx->after_multithreaded_fork = true; |
891 | thr->ignore_interceptors++; |
892 | thr->suppress_reports++; |
893 | ThreadIgnoreBegin(thr, pc); |
894 | ThreadIgnoreSyncBegin(thr, pc); |
895 | } |
896 | } |
897 | #endif |
898 | |
899 | #if SANITIZER_GO |
900 | NOINLINE |
901 | void GrowShadowStack(ThreadState *thr) { |
902 | const int sz = thr->shadow_stack_end - thr->shadow_stack; |
903 | const int newsz = 2 * sz; |
904 | auto *newstack = (uptr *)Alloc(newsz * sizeof(uptr)); |
905 | internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr)); |
906 | Free(thr->shadow_stack); |
907 | thr->shadow_stack = newstack; |
908 | thr->shadow_stack_pos = newstack + sz; |
909 | thr->shadow_stack_end = newstack + newsz; |
910 | } |
911 | #endif |
912 | |
913 | StackID CurrentStackId(ThreadState *thr, uptr pc) { |
914 | #if !SANITIZER_GO |
915 | if (!thr->is_inited) // May happen during bootstrap. |
916 | return kInvalidStackID; |
917 | #endif |
918 | if (pc != 0) { |
919 | #if !SANITIZER_GO |
920 | DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); |
921 | #else |
922 | if (thr->shadow_stack_pos == thr->shadow_stack_end) |
923 | GrowShadowStack(thr); |
924 | #endif |
925 | thr->shadow_stack_pos[0] = pc; |
926 | thr->shadow_stack_pos++; |
927 | } |
928 | StackID id = StackDepotPut( |
929 | stack: StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack)); |
930 | if (pc != 0) |
931 | thr->shadow_stack_pos--; |
932 | return id; |
933 | } |
934 | |
935 | static bool TraceSkipGap(ThreadState* thr) { |
936 | Trace *trace = &thr->tctx->trace; |
937 | Event *pos = reinterpret_cast<Event *>(atomic_load_relaxed(a: &thr->trace_pos)); |
938 | DCHECK_EQ(reinterpret_cast<uptr>(pos + 1) & TracePart::kAlignment, 0); |
939 | auto *part = trace->parts.Back(); |
940 | DPrintf("#%d: TraceSwitchPart enter trace=%p parts=%p-%p pos=%p\n" , thr->tid, |
941 | trace, trace->parts.Front(), part, pos); |
942 | if (!part) |
943 | return false; |
944 | // We can get here when we still have space in the current trace part. |
945 | // The fast-path check in TraceAcquire has false positives in the middle of |
946 | // the part. Check if we are indeed at the end of the current part or not, |
947 | // and fill any gaps with NopEvent's. |
948 | Event* end = &part->events[TracePart::kSize]; |
949 | DCHECK_GE(pos, &part->events[0]); |
950 | DCHECK_LE(pos, end); |
951 | if (pos + 1 < end) { |
952 | if ((reinterpret_cast<uptr>(pos) & TracePart::kAlignment) == |
953 | TracePart::kAlignment) |
954 | *pos++ = NopEvent; |
955 | *pos++ = NopEvent; |
956 | DCHECK_LE(pos + 2, end); |
957 | atomic_store_relaxed(a: &thr->trace_pos, v: reinterpret_cast<uptr>(pos)); |
958 | return true; |
959 | } |
960 | // We are indeed at the end. |
961 | for (; pos < end; pos++) *pos = NopEvent; |
962 | return false; |
963 | } |
964 | |
965 | NOINLINE |
966 | void TraceSwitchPart(ThreadState* thr) { |
967 | if (TraceSkipGap(thr)) |
968 | return; |
969 | #if !SANITIZER_GO |
970 | if (ctx->after_multithreaded_fork) { |
971 | // We just need to survive till exec. |
972 | TracePart* part = thr->tctx->trace.parts.Back(); |
973 | if (part) { |
974 | atomic_store_relaxed(a: &thr->trace_pos, |
975 | v: reinterpret_cast<uptr>(&part->events[0])); |
976 | return; |
977 | } |
978 | } |
979 | #endif |
980 | TraceSwitchPartImpl(thr); |
981 | } |
982 | |
983 | void TraceSwitchPartImpl(ThreadState* thr) { |
984 | SlotLocker locker(thr, true); |
985 | Trace* trace = &thr->tctx->trace; |
986 | TracePart* part = TracePartAlloc(thr); |
987 | part->trace = trace; |
988 | thr->trace_prev_pc = 0; |
989 | TracePart* recycle = nullptr; |
990 | // Keep roughly half of parts local to the thread |
991 | // (not queued into the recycle queue). |
992 | uptr local_parts = (Trace::kMinParts + flags()->history_size + 1) / 2; |
993 | { |
994 | Lock lock(&trace->mtx); |
995 | if (trace->parts.Empty()) |
996 | trace->local_head = part; |
997 | if (trace->parts.Size() >= local_parts) { |
998 | recycle = trace->local_head; |
999 | trace->local_head = trace->parts.Next(e: recycle); |
1000 | } |
1001 | trace->parts.PushBack(e: part); |
1002 | atomic_store_relaxed(a: &thr->trace_pos, |
1003 | v: reinterpret_cast<uptr>(&part->events[0])); |
1004 | } |
1005 | // Make this part self-sufficient by restoring the current stack |
1006 | // and mutex set in the beginning of the trace. |
1007 | TraceTime(thr); |
1008 | { |
1009 | // Pathologically large stacks may not fit into the part. |
1010 | // In these cases we log only fixed number of top frames. |
1011 | const uptr kMaxFrames = 1000; |
1012 | // Check that kMaxFrames won't consume the whole part. |
1013 | static_assert(kMaxFrames < TracePart::kSize / 2, "kMaxFrames is too big" ); |
1014 | uptr* pos = Max(a: &thr->shadow_stack[0], b: thr->shadow_stack_pos - kMaxFrames); |
1015 | for (; pos < thr->shadow_stack_pos; pos++) { |
1016 | if (TryTraceFunc(thr, pc: *pos)) |
1017 | continue; |
1018 | CHECK(TraceSkipGap(thr)); |
1019 | CHECK(TryTraceFunc(thr, *pos)); |
1020 | } |
1021 | } |
1022 | for (uptr i = 0; i < thr->mset.Size(); i++) { |
1023 | MutexSet::Desc d = thr->mset.Get(i); |
1024 | for (uptr i = 0; i < d.count; i++) |
1025 | TraceMutexLock(thr, type: d.write ? EventType::kLock : EventType::kRLock, pc: 0, |
1026 | addr: d.addr, stk: d.stack_id); |
1027 | } |
1028 | // Callers of TraceSwitchPart expect that TraceAcquire will always succeed |
1029 | // after the call. It's possible that TryTraceFunc/TraceMutexLock above |
1030 | // filled the trace part exactly up to the TracePart::kAlignment gap |
1031 | // and the next TraceAcquire won't succeed. Skip the gap to avoid that. |
1032 | EventFunc *ev; |
1033 | if (!TraceAcquire(thr, ev: &ev)) { |
1034 | CHECK(TraceSkipGap(thr)); |
1035 | CHECK(TraceAcquire(thr, &ev)); |
1036 | } |
1037 | { |
1038 | Lock lock(&ctx->slot_mtx); |
1039 | // There is a small chance that the slot may be not queued at this point. |
1040 | // This can happen if the slot has kEpochLast epoch and another thread |
1041 | // in FindSlotAndLock discovered that it's exhausted and removed it from |
1042 | // the slot queue. kEpochLast can happen in 2 cases: (1) if TraceSwitchPart |
1043 | // was called with the slot locked and epoch already at kEpochLast, |
1044 | // or (2) if we've acquired a new slot in SlotLock in the beginning |
1045 | // of the function and the slot was at kEpochLast - 1, so after increment |
1046 | // in SlotAttachAndLock it become kEpochLast. |
1047 | if (ctx->slot_queue.Queued(e: thr->slot)) { |
1048 | ctx->slot_queue.Remove(e: thr->slot); |
1049 | ctx->slot_queue.PushBack(e: thr->slot); |
1050 | } |
1051 | if (recycle) |
1052 | ctx->trace_part_recycle.PushBack(e: recycle); |
1053 | } |
1054 | DPrintf("#%d: TraceSwitchPart exit parts=%p-%p pos=0x%zx\n" , thr->tid, |
1055 | trace->parts.Front(), trace->parts.Back(), |
1056 | atomic_load_relaxed(&thr->trace_pos)); |
1057 | } |
1058 | |
1059 | void ThreadIgnoreBegin(ThreadState* thr, uptr pc) { |
1060 | DPrintf("#%d: ThreadIgnoreBegin\n" , thr->tid); |
1061 | thr->ignore_reads_and_writes++; |
1062 | CHECK_GT(thr->ignore_reads_and_writes, 0); |
1063 | thr->fast_state.SetIgnoreBit(); |
1064 | #if !SANITIZER_GO |
1065 | if (pc && !ctx->after_multithreaded_fork) |
1066 | thr->mop_ignore_set.Add(stack_id: CurrentStackId(thr, pc)); |
1067 | #endif |
1068 | } |
1069 | |
1070 | void ThreadIgnoreEnd(ThreadState *thr) { |
1071 | DPrintf("#%d: ThreadIgnoreEnd\n" , thr->tid); |
1072 | CHECK_GT(thr->ignore_reads_and_writes, 0); |
1073 | thr->ignore_reads_and_writes--; |
1074 | if (thr->ignore_reads_and_writes == 0) { |
1075 | thr->fast_state.ClearIgnoreBit(); |
1076 | #if !SANITIZER_GO |
1077 | thr->mop_ignore_set.Reset(); |
1078 | #endif |
1079 | } |
1080 | } |
1081 | |
1082 | #if !SANITIZER_GO |
1083 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
1084 | uptr __tsan_testonly_shadow_stack_current_size() { |
1085 | ThreadState *thr = cur_thread(); |
1086 | return thr->shadow_stack_pos - thr->shadow_stack; |
1087 | } |
1088 | #endif |
1089 | |
1090 | void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { |
1091 | DPrintf("#%d: ThreadIgnoreSyncBegin\n" , thr->tid); |
1092 | thr->ignore_sync++; |
1093 | CHECK_GT(thr->ignore_sync, 0); |
1094 | #if !SANITIZER_GO |
1095 | if (pc && !ctx->after_multithreaded_fork) |
1096 | thr->sync_ignore_set.Add(stack_id: CurrentStackId(thr, pc)); |
1097 | #endif |
1098 | } |
1099 | |
1100 | void ThreadIgnoreSyncEnd(ThreadState *thr) { |
1101 | DPrintf("#%d: ThreadIgnoreSyncEnd\n" , thr->tid); |
1102 | CHECK_GT(thr->ignore_sync, 0); |
1103 | thr->ignore_sync--; |
1104 | #if !SANITIZER_GO |
1105 | if (thr->ignore_sync == 0) |
1106 | thr->sync_ignore_set.Reset(); |
1107 | #endif |
1108 | } |
1109 | |
1110 | bool MD5Hash::operator==(const MD5Hash &other) const { |
1111 | return hash[0] == other.hash[0] && hash[1] == other.hash[1]; |
1112 | } |
1113 | |
1114 | #if SANITIZER_DEBUG |
1115 | void build_consistency_debug() {} |
1116 | #else |
1117 | void build_consistency_release() {} |
1118 | #endif |
1119 | } // namespace __tsan |
1120 | |
1121 | #if SANITIZER_CHECK_DEADLOCKS |
1122 | namespace __sanitizer { |
1123 | using namespace __tsan; |
1124 | MutexMeta mutex_meta[] = { |
1125 | {MutexInvalid, "Invalid" , {}}, |
1126 | {MutexThreadRegistry, |
1127 | "ThreadRegistry" , |
1128 | {MutexTypeSlots, MutexTypeTrace, MutexTypeReport}}, |
1129 | {MutexTypeReport, "Report" , {MutexTypeTrace}}, |
1130 | {MutexTypeSyncVar, "SyncVar" , {MutexTypeReport, MutexTypeTrace}}, |
1131 | {MutexTypeAnnotations, "Annotations" , {}}, |
1132 | {MutexTypeAtExit, "AtExit" , {}}, |
1133 | {MutexTypeFired, "Fired" , {MutexLeaf}}, |
1134 | {MutexTypeRacy, "Racy" , {MutexLeaf}}, |
1135 | {MutexTypeGlobalProc, "GlobalProc" , {MutexTypeSlot, MutexTypeSlots}}, |
1136 | {MutexTypeInternalAlloc, "InternalAlloc" , {MutexLeaf}}, |
1137 | {MutexTypeTrace, "Trace" , {}}, |
1138 | {MutexTypeSlot, |
1139 | "Slot" , |
1140 | {MutexMulti, MutexTypeTrace, MutexTypeSyncVar, MutexThreadRegistry, |
1141 | MutexTypeSlots}}, |
1142 | {MutexTypeSlots, "Slots" , {MutexTypeTrace, MutexTypeReport}}, |
1143 | {}, |
1144 | }; |
1145 | |
1146 | void PrintMutexPC(uptr pc) { StackTrace(&pc, 1).Print(); } |
1147 | |
1148 | } // namespace __sanitizer |
1149 | #endif |
1150 | |