1 | //===-- tsan_suppressions.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 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "tsan_suppressions.h" |
14 | |
15 | #include "sanitizer_common/sanitizer_common.h" |
16 | #include "sanitizer_common/sanitizer_libc.h" |
17 | #include "sanitizer_common/sanitizer_placement_new.h" |
18 | #include "sanitizer_common/sanitizer_suppressions.h" |
19 | #include "tsan_flags.h" |
20 | #include "tsan_mman.h" |
21 | #include "tsan_platform.h" |
22 | #include "tsan_rtl.h" |
23 | |
24 | #if !SANITIZER_GO |
25 | // Suppressions for true/false positives in standard libraries. |
26 | static const char *const std_suppressions = |
27 | // Libstdc++ 4.4 has data races in std::string. |
28 | // See http://crbug.com/181502 for an example. |
29 | "race:^_M_rep$\n" |
30 | "race:^_M_is_leaked$\n" |
31 | // False positive when using std <thread>. |
32 | // Happens because we miss atomic synchronization in libstdc++. |
33 | // See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. |
34 | "race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n" ; |
35 | |
36 | // Can be overriden in frontend. |
37 | SANITIZER_WEAK_DEFAULT_IMPL |
38 | const char *__tsan_default_suppressions() { |
39 | return 0; |
40 | } |
41 | #endif |
42 | |
43 | namespace __tsan { |
44 | |
45 | alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)]; |
46 | static SuppressionContext *suppression_ctx = nullptr; |
47 | static const char *kSuppressionTypes[] = { |
48 | kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex, |
49 | kSuppressionThread, kSuppressionSignal, kSuppressionLib, |
50 | kSuppressionDeadlock}; |
51 | |
52 | void InitializeSuppressions() { |
53 | CHECK_EQ(nullptr, suppression_ctx); |
54 | suppression_ctx = new (suppression_placeholder) |
55 | SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); |
56 | suppression_ctx->ParseFromFile(filename: flags()->suppressions); |
57 | #if !SANITIZER_GO |
58 | suppression_ctx->Parse(str: __tsan_default_suppressions()); |
59 | suppression_ctx->Parse(str: std_suppressions); |
60 | #endif |
61 | } |
62 | |
63 | SuppressionContext *Suppressions() { |
64 | CHECK(suppression_ctx); |
65 | return suppression_ctx; |
66 | } |
67 | |
68 | static const char *conv(ReportType typ) { |
69 | switch (typ) { |
70 | case ReportTypeRace: |
71 | case ReportTypeVptrRace: |
72 | case ReportTypeUseAfterFree: |
73 | case ReportTypeVptrUseAfterFree: |
74 | case ReportTypeExternalRace: |
75 | return kSuppressionRace; |
76 | case ReportTypeThreadLeak: |
77 | return kSuppressionThread; |
78 | case ReportTypeMutexDestroyLocked: |
79 | case ReportTypeMutexDoubleLock: |
80 | case ReportTypeMutexInvalidAccess: |
81 | case ReportTypeMutexBadUnlock: |
82 | case ReportTypeMutexBadReadLock: |
83 | case ReportTypeMutexBadReadUnlock: |
84 | case ReportTypeMutexHeldWrongContext: |
85 | return kSuppressionMutex; |
86 | case ReportTypeSignalUnsafe: |
87 | case ReportTypeErrnoInSignal: |
88 | return kSuppressionSignal; |
89 | case ReportTypeDeadlock: |
90 | return kSuppressionDeadlock; |
91 | // No default case so compiler warns us if we miss one |
92 | } |
93 | UNREACHABLE("missing case" ); |
94 | } |
95 | |
96 | static uptr IsSuppressed(const char *stype, const AddressInfo &info, |
97 | Suppression **sp) { |
98 | if (suppression_ctx->Match(str: info.function, type: stype, s: sp) || |
99 | suppression_ctx->Match(str: info.file, type: stype, s: sp) || |
100 | suppression_ctx->Match(str: info.module, type: stype, s: sp)) { |
101 | VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n" , (*sp)->templ); |
102 | atomic_fetch_add(a: &(*sp)->hit_count, v: 1, mo: memory_order_relaxed); |
103 | return info.address; |
104 | } |
105 | return 0; |
106 | } |
107 | |
108 | uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { |
109 | CHECK(suppression_ctx); |
110 | if (!suppression_ctx->SuppressionCount() || stack == 0 || |
111 | !stack->suppressable) |
112 | return 0; |
113 | const char *stype = conv(typ); |
114 | if (0 == internal_strcmp(s1: stype, s2: kSuppressionNone)) |
115 | return 0; |
116 | for (const SymbolizedStack *frame = stack->frames; frame; |
117 | frame = frame->next) { |
118 | uptr pc = IsSuppressed(stype, info: frame->info, sp); |
119 | if (pc != 0) |
120 | return pc; |
121 | } |
122 | if (0 == internal_strcmp(s1: stype, s2: kSuppressionRace) && stack->frames != nullptr) |
123 | return IsSuppressed(stype: kSuppressionRaceTop, info: stack->frames->info, sp); |
124 | return 0; |
125 | } |
126 | |
127 | uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { |
128 | CHECK(suppression_ctx); |
129 | if (!suppression_ctx->SuppressionCount() || loc == 0 || |
130 | loc->type != ReportLocationGlobal || !loc->suppressable) |
131 | return 0; |
132 | const char *stype = conv(typ); |
133 | if (0 == internal_strcmp(s1: stype, s2: kSuppressionNone)) |
134 | return 0; |
135 | Suppression *s; |
136 | const DataInfo &global = loc->global; |
137 | if (suppression_ctx->Match(str: global.name, type: stype, s: &s) || |
138 | suppression_ctx->Match(str: global.module, type: stype, s: &s)) { |
139 | VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n" , s->templ); |
140 | atomic_fetch_add(a: &s->hit_count, v: 1, mo: memory_order_relaxed); |
141 | *sp = s; |
142 | return global.start; |
143 | } |
144 | return 0; |
145 | } |
146 | |
147 | void PrintMatchedSuppressions() { |
148 | InternalMmapVector<Suppression *> matched; |
149 | CHECK(suppression_ctx); |
150 | suppression_ctx->GetMatched(matched: &matched); |
151 | if (!matched.size()) |
152 | return; |
153 | int hit_count = 0; |
154 | for (uptr i = 0; i < matched.size(); i++) |
155 | hit_count += atomic_load_relaxed(a: &matched[i]->hit_count); |
156 | Printf(format: "ThreadSanitizer: Matched %d suppressions (pid=%d):\n" , hit_count, |
157 | (int)internal_getpid()); |
158 | for (uptr i = 0; i < matched.size(); i++) { |
159 | Printf(format: "%d %s:%s\n" , atomic_load_relaxed(a: &matched[i]->hit_count), |
160 | matched[i]->type, matched[i]->templ); |
161 | } |
162 | } |
163 | } // namespace __tsan |
164 | |