]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- msan_report.cc ----------------------------------------------------===// |
2 | // | |
3 | // The LLVM Compiler Infrastructure | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | // | |
10 | // This file is a part of MemorySanitizer. | |
11 | // | |
12 | // Error reporting. | |
13 | //===----------------------------------------------------------------------===// | |
14 | ||
15 | #include "msan.h" | |
92a42be0 SL |
16 | #include "msan_chained_origin_depot.h" |
17 | #include "msan_origin.h" | |
1a4d82fc JJ |
18 | #include "sanitizer_common/sanitizer_allocator_internal.h" |
19 | #include "sanitizer_common/sanitizer_common.h" | |
20 | #include "sanitizer_common/sanitizer_flags.h" | |
21 | #include "sanitizer_common/sanitizer_mutex.h" | |
22 | #include "sanitizer_common/sanitizer_report_decorator.h" | |
23 | #include "sanitizer_common/sanitizer_stackdepot.h" | |
24 | #include "sanitizer_common/sanitizer_symbolizer.h" | |
25 | ||
26 | using namespace __sanitizer; | |
27 | ||
28 | namespace __msan { | |
29 | ||
92a42be0 | 30 | class Decorator: public __sanitizer::SanitizerCommonDecorator { |
1a4d82fc | 31 | public: |
92a42be0 | 32 | Decorator() : SanitizerCommonDecorator() { } |
1a4d82fc JJ |
33 | const char *Warning() { return Red(); } |
34 | const char *Origin() { return Magenta(); } | |
35 | const char *Name() { return Green(); } | |
36 | const char *End() { return Default(); } | |
37 | }; | |
38 | ||
39 | static void DescribeStackOrigin(const char *so, uptr pc) { | |
40 | Decorator d; | |
41 | char *s = internal_strdup(so); | |
42 | char *sep = internal_strchr(s, '@'); | |
43 | CHECK(sep); | |
44 | *sep = '\0'; | |
45 | Printf("%s", d.Origin()); | |
46 | Printf( | |
47 | " %sUninitialized value was created by an allocation of '%s%s%s'" | |
48 | " in the stack frame of function '%s%s%s'%s\n", | |
92a42be0 SL |
49 | d.Origin(), d.Name(), s, d.Origin(), d.Name(), sep + 1, d.Origin(), |
50 | d.End()); | |
1a4d82fc JJ |
51 | InternalFree(s); |
52 | ||
53 | if (pc) { | |
54 | // For some reason function address in LLVM IR is 1 less then the address | |
55 | // of the first instruction. | |
92a42be0 SL |
56 | pc = StackTrace::GetNextInstructionPc(pc); |
57 | StackTrace(&pc, 1).Print(); | |
1a4d82fc JJ |
58 | } |
59 | } | |
60 | ||
92a42be0 SL |
61 | static void DescribeOrigin(u32 id) { |
62 | VPrintf(1, " raw origin id: %d\n", id); | |
63 | Decorator d; | |
64 | Origin o = Origin::FromRawId(id); | |
65 | while (o.isChainedOrigin()) { | |
66 | StackTrace stack; | |
67 | o = o.getNextChainedOrigin(&stack); | |
68 | Printf(" %sUninitialized value was stored to memory at%s\n", d.Origin(), | |
69 | d.End()); | |
70 | stack.Print(); | |
71 | } | |
72 | if (o.isStackOrigin()) { | |
73 | uptr pc; | |
74 | const char *so = GetStackOriginDescr(o.getStackId(), &pc); | |
75 | DescribeStackOrigin(so, pc); | |
76 | } else { | |
77 | StackTrace stack = o.getStackTraceForHeapOrigin(); | |
78 | switch (stack.tag) { | |
79 | case StackTrace::TAG_ALLOC: | |
80 | Printf(" %sUninitialized value was created by a heap allocation%s\n", | |
81 | d.Origin(), d.End()); | |
82 | break; | |
83 | case StackTrace::TAG_DEALLOC: | |
84 | Printf(" %sUninitialized value was created by a heap deallocation%s\n", | |
85 | d.Origin(), d.End()); | |
86 | break; | |
87 | case STACK_TRACE_TAG_POISON: | |
88 | Printf(" %sMemory was marked as uninitialized%s\n", d.Origin(), | |
89 | d.End()); | |
90 | break; | |
91 | default: | |
92 | Printf(" %sUninitialized value was created%s\n", d.Origin(), d.End()); | |
93 | break; | |
1a4d82fc | 94 | } |
92a42be0 | 95 | stack.Print(); |
1a4d82fc JJ |
96 | } |
97 | } | |
98 | ||
99 | void ReportUMR(StackTrace *stack, u32 origin) { | |
100 | if (!__msan::flags()->report_umrs) return; | |
101 | ||
102 | SpinMutexLock l(&CommonSanitizerReportMutex); | |
103 | ||
104 | Decorator d; | |
105 | Printf("%s", d.Warning()); | |
92a42be0 | 106 | Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n"); |
1a4d82fc JJ |
107 | Printf("%s", d.End()); |
108 | stack->Print(); | |
109 | if (origin) { | |
110 | DescribeOrigin(origin); | |
111 | } | |
112 | ReportErrorSummary("use-of-uninitialized-value", stack); | |
113 | } | |
114 | ||
115 | void ReportExpectedUMRNotFound(StackTrace *stack) { | |
116 | SpinMutexLock l(&CommonSanitizerReportMutex); | |
117 | ||
92a42be0 | 118 | Printf("WARNING: Expected use of uninitialized value not found\n"); |
1a4d82fc JJ |
119 | stack->Print(); |
120 | } | |
121 | ||
92a42be0 SL |
122 | void ReportStats() { |
123 | SpinMutexLock l(&CommonSanitizerReportMutex); | |
124 | ||
125 | if (__msan_get_track_origins() > 0) { | |
126 | StackDepotStats *stack_depot_stats = StackDepotGetStats(); | |
127 | // FIXME: we want this at normal exit, too! | |
128 | // FIXME: but only with verbosity=1 or something | |
129 | Printf("Unique heap origins: %zu\n", stack_depot_stats->n_uniq_ids); | |
130 | Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats->allocated); | |
131 | ||
132 | StackDepotStats *chained_origin_depot_stats = ChainedOriginDepotGetStats(); | |
133 | Printf("Unique origin histories: %zu\n", | |
134 | chained_origin_depot_stats->n_uniq_ids); | |
135 | Printf("History depot allocated bytes: %zu\n", | |
136 | chained_origin_depot_stats->allocated); | |
137 | } | |
138 | } | |
139 | ||
1a4d82fc JJ |
140 | void ReportAtExitStatistics() { |
141 | SpinMutexLock l(&CommonSanitizerReportMutex); | |
142 | ||
143 | if (msan_report_count > 0) { | |
144 | Decorator d; | |
145 | Printf("%s", d.Warning()); | |
146 | Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); | |
147 | Printf("%s", d.End()); | |
148 | } | |
1a4d82fc JJ |
149 | } |
150 | ||
151 | class OriginSet { | |
152 | public: | |
153 | OriginSet() : next_id_(0) {} | |
154 | int insert(u32 o) { | |
155 | // Scan from the end for better locality. | |
156 | for (int i = next_id_ - 1; i >= 0; --i) | |
157 | if (origins_[i] == o) return i; | |
158 | if (next_id_ == kMaxSize_) return OVERFLOW; | |
159 | int id = next_id_++; | |
160 | origins_[id] = o; | |
161 | return id; | |
162 | } | |
163 | int size() { return next_id_; } | |
164 | u32 get(int id) { return origins_[id]; } | |
165 | static char asChar(int id) { | |
166 | switch (id) { | |
167 | case MISSING: | |
168 | return '.'; | |
169 | case OVERFLOW: | |
170 | return '*'; | |
171 | default: | |
172 | return 'A' + id; | |
173 | } | |
174 | } | |
175 | static const int OVERFLOW = -1; | |
176 | static const int MISSING = -2; | |
177 | ||
178 | private: | |
179 | static const int kMaxSize_ = 'Z' - 'A' + 1; | |
180 | u32 origins_[kMaxSize_]; | |
181 | int next_id_; | |
182 | }; | |
183 | ||
184 | void DescribeMemoryRange(const void *x, uptr size) { | |
185 | // Real limits. | |
186 | uptr start = MEM_TO_SHADOW(x); | |
187 | uptr end = start + size; | |
188 | // Scan limits: align start down to 4; align size up to 16. | |
189 | uptr s = start & ~3UL; | |
190 | size = end - s; | |
191 | size = (size + 15) & ~15UL; | |
192 | uptr e = s + size; | |
193 | ||
194 | // Single letter names to origin id mapping. | |
195 | OriginSet origin_set; | |
196 | ||
197 | uptr pos = 0; // Offset from aligned start. | |
198 | bool with_origins = __msan_get_track_origins(); | |
199 | // True if there is at least 1 poisoned bit in the last 4-byte group. | |
200 | bool last_quad_poisoned; | |
201 | int origin_ids[4]; // Single letter origin ids for the current line. | |
202 | ||
203 | Decorator d; | |
204 | Printf("%s", d.Warning()); | |
205 | Printf("Shadow map of [%p, %p), %zu bytes:\n", start, end, end - start); | |
206 | Printf("%s", d.End()); | |
207 | while (s < e) { | |
208 | // Line start. | |
209 | if (pos % 16 == 0) { | |
210 | for (int i = 0; i < 4; ++i) origin_ids[i] = -1; | |
211 | Printf("%p:", s); | |
212 | } | |
213 | // Group start. | |
214 | if (pos % 4 == 0) { | |
215 | Printf(" "); | |
216 | last_quad_poisoned = false; | |
217 | } | |
218 | // Print shadow byte. | |
219 | if (s < start || s >= end) { | |
220 | Printf(".."); | |
221 | } else { | |
222 | unsigned char v = *(unsigned char *)s; | |
223 | if (v) last_quad_poisoned = true; | |
92a42be0 SL |
224 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
225 | Printf("%x%x", v & 0xf, v >> 4); | |
226 | #else | |
227 | Printf("%x%x", v >> 4, v & 0xf); | |
228 | #endif | |
1a4d82fc JJ |
229 | } |
230 | // Group end. | |
231 | if (pos % 4 == 3 && with_origins) { | |
232 | int id = OriginSet::MISSING; | |
233 | if (last_quad_poisoned) { | |
234 | u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3); | |
235 | id = origin_set.insert(o); | |
236 | } | |
237 | origin_ids[(pos % 16) / 4] = id; | |
238 | } | |
239 | // Line end. | |
240 | if (pos % 16 == 15) { | |
241 | if (with_origins) { | |
242 | Printf(" |"); | |
243 | for (int i = 0; i < 4; ++i) { | |
244 | char c = OriginSet::asChar(origin_ids[i]); | |
245 | Printf("%c", c); | |
246 | if (i != 3) Printf(" "); | |
247 | } | |
248 | Printf("|"); | |
249 | } | |
250 | Printf("\n"); | |
251 | } | |
252 | size--; | |
253 | s++; | |
254 | pos++; | |
255 | } | |
256 | ||
257 | Printf("\n"); | |
258 | ||
259 | for (int i = 0; i < origin_set.size(); ++i) { | |
260 | u32 o = origin_set.get(i); | |
261 | Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o); | |
262 | DescribeOrigin(o); | |
263 | } | |
264 | } | |
265 | ||
92a42be0 SL |
266 | void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, |
267 | uptr offset) { | |
268 | Decorator d; | |
269 | Printf("%s", d.Warning()); | |
270 | Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n", | |
271 | d.Warning(), d.Name(), what, d.Warning(), offset, start, size, | |
272 | d.End()); | |
273 | if (__sanitizer::Verbosity()) | |
274 | DescribeMemoryRange(start, size); | |
275 | } | |
276 | ||
1a4d82fc | 277 | } // namespace __msan |