]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- asan_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 AddressSanitizer, an address sanity checker. | |
11 | // | |
12 | // This file contains error reporting code. | |
13 | //===----------------------------------------------------------------------===// | |
92a42be0 | 14 | |
1a4d82fc JJ |
15 | #include "asan_flags.h" |
16 | #include "asan_internal.h" | |
17 | #include "asan_mapping.h" | |
18 | #include "asan_report.h" | |
5bcae85e | 19 | #include "asan_scariness_score.h" |
1a4d82fc JJ |
20 | #include "asan_stack.h" |
21 | #include "asan_thread.h" | |
22 | #include "sanitizer_common/sanitizer_common.h" | |
23 | #include "sanitizer_common/sanitizer_flags.h" | |
24 | #include "sanitizer_common/sanitizer_report_decorator.h" | |
25 | #include "sanitizer_common/sanitizer_stackdepot.h" | |
26 | #include "sanitizer_common/sanitizer_symbolizer.h" | |
27 | ||
28 | namespace __asan { | |
29 | ||
30 | // -------------------- User-specified callbacks ----------------- {{{1 | |
31 | static void (*error_report_callback)(const char*); | |
92a42be0 | 32 | static char *error_message_buffer = nullptr; |
1a4d82fc | 33 | static uptr error_message_buffer_pos = 0; |
3157f602 XL |
34 | static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); |
35 | static const unsigned kAsanBuggyPcPoolSize = 25; | |
36 | static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; | |
1a4d82fc | 37 | |
92a42be0 SL |
38 | struct ReportData { |
39 | uptr pc; | |
40 | uptr sp; | |
41 | uptr bp; | |
42 | uptr addr; | |
43 | bool is_write; | |
44 | uptr access_size; | |
45 | const char *description; | |
46 | }; | |
47 | ||
48 | static bool report_happened = false; | |
49 | static ReportData report_data = {}; | |
50 | ||
1a4d82fc | 51 | void AppendToErrorMessageBuffer(const char *buffer) { |
3157f602 XL |
52 | BlockingMutexLock l(&error_message_buf_mutex); |
53 | if (!error_message_buffer) { | |
54 | error_message_buffer = | |
55 | (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); | |
56 | error_message_buffer_pos = 0; | |
1a4d82fc | 57 | } |
3157f602 XL |
58 | uptr length = internal_strlen(buffer); |
59 | RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); | |
60 | uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; | |
61 | internal_strncpy(error_message_buffer + error_message_buffer_pos, | |
62 | buffer, remaining); | |
63 | error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; | |
64 | // FIXME: reallocate the buffer instead of truncating the message. | |
65 | error_message_buffer_pos += Min(remaining, length); | |
1a4d82fc JJ |
66 | } |
67 | ||
68 | // ---------------------- Decorator ------------------------------ {{{1 | |
69 | class Decorator: public __sanitizer::SanitizerCommonDecorator { | |
70 | public: | |
71 | Decorator() : SanitizerCommonDecorator() { } | |
72 | const char *Access() { return Blue(); } | |
73 | const char *EndAccess() { return Default(); } | |
74 | const char *Location() { return Green(); } | |
75 | const char *EndLocation() { return Default(); } | |
76 | const char *Allocation() { return Magenta(); } | |
77 | const char *EndAllocation() { return Default(); } | |
78 | ||
79 | const char *ShadowByte(u8 byte) { | |
80 | switch (byte) { | |
81 | case kAsanHeapLeftRedzoneMagic: | |
82 | case kAsanHeapRightRedzoneMagic: | |
92a42be0 | 83 | case kAsanArrayCookieMagic: |
1a4d82fc JJ |
84 | return Red(); |
85 | case kAsanHeapFreeMagic: | |
86 | return Magenta(); | |
87 | case kAsanStackLeftRedzoneMagic: | |
88 | case kAsanStackMidRedzoneMagic: | |
89 | case kAsanStackRightRedzoneMagic: | |
90 | case kAsanStackPartialRedzoneMagic: | |
91 | return Red(); | |
92 | case kAsanStackAfterReturnMagic: | |
93 | return Magenta(); | |
94 | case kAsanInitializationOrderMagic: | |
95 | return Cyan(); | |
96 | case kAsanUserPoisonedMemoryMagic: | |
97 | case kAsanContiguousContainerOOBMagic: | |
92a42be0 SL |
98 | case kAsanAllocaLeftMagic: |
99 | case kAsanAllocaRightMagic: | |
1a4d82fc JJ |
100 | return Blue(); |
101 | case kAsanStackUseAfterScopeMagic: | |
102 | return Magenta(); | |
103 | case kAsanGlobalRedzoneMagic: | |
104 | return Red(); | |
105 | case kAsanInternalHeapMagic: | |
106 | return Yellow(); | |
92a42be0 SL |
107 | case kAsanIntraObjectRedzone: |
108 | return Yellow(); | |
1a4d82fc JJ |
109 | default: |
110 | return Default(); | |
111 | } | |
112 | } | |
113 | const char *EndShadowByte() { return Default(); } | |
92a42be0 SL |
114 | const char *MemoryByte() { return Magenta(); } |
115 | const char *EndMemoryByte() { return Default(); } | |
1a4d82fc JJ |
116 | }; |
117 | ||
118 | // ---------------------- Helper functions ----------------------- {{{1 | |
119 | ||
92a42be0 SL |
120 | static void PrintMemoryByte(InternalScopedString *str, const char *before, |
121 | u8 byte, bool in_shadow, const char *after = "\n") { | |
1a4d82fc | 122 | Decorator d; |
92a42be0 SL |
123 | str->append("%s%s%x%x%s%s", before, |
124 | in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), | |
125 | byte >> 4, byte & 15, | |
126 | in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after); | |
127 | } | |
128 | ||
129 | static void PrintShadowByte(InternalScopedString *str, const char *before, | |
130 | u8 byte, const char *after = "\n") { | |
131 | PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); | |
1a4d82fc JJ |
132 | } |
133 | ||
134 | static void PrintShadowBytes(InternalScopedString *str, const char *before, | |
135 | u8 *bytes, u8 *guilty, uptr n) { | |
136 | Decorator d; | |
137 | if (before) str->append("%s%p:", before, bytes); | |
138 | for (uptr i = 0; i < n; i++) { | |
139 | u8 *p = bytes + i; | |
140 | const char *before = | |
141 | p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; | |
142 | const char *after = p == guilty ? "]" : ""; | |
143 | PrintShadowByte(str, before, *p, after); | |
144 | } | |
145 | str->append("\n"); | |
146 | } | |
147 | ||
148 | static void PrintLegend(InternalScopedString *str) { | |
149 | str->append( | |
150 | "Shadow byte legend (one shadow byte represents %d " | |
151 | "application bytes):\n", | |
152 | (int)SHADOW_GRANULARITY); | |
153 | PrintShadowByte(str, " Addressable: ", 0); | |
154 | str->append(" Partially addressable: "); | |
155 | for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); | |
156 | str->append("\n"); | |
157 | PrintShadowByte(str, " Heap left redzone: ", | |
158 | kAsanHeapLeftRedzoneMagic); | |
159 | PrintShadowByte(str, " Heap right redzone: ", | |
160 | kAsanHeapRightRedzoneMagic); | |
161 | PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); | |
162 | PrintShadowByte(str, " Stack left redzone: ", | |
163 | kAsanStackLeftRedzoneMagic); | |
164 | PrintShadowByte(str, " Stack mid redzone: ", | |
165 | kAsanStackMidRedzoneMagic); | |
166 | PrintShadowByte(str, " Stack right redzone: ", | |
167 | kAsanStackRightRedzoneMagic); | |
168 | PrintShadowByte(str, " Stack partial redzone: ", | |
169 | kAsanStackPartialRedzoneMagic); | |
170 | PrintShadowByte(str, " Stack after return: ", | |
171 | kAsanStackAfterReturnMagic); | |
172 | PrintShadowByte(str, " Stack use after scope: ", | |
173 | kAsanStackUseAfterScopeMagic); | |
174 | PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); | |
175 | PrintShadowByte(str, " Global init order: ", | |
176 | kAsanInitializationOrderMagic); | |
177 | PrintShadowByte(str, " Poisoned by user: ", | |
178 | kAsanUserPoisonedMemoryMagic); | |
179 | PrintShadowByte(str, " Container overflow: ", | |
180 | kAsanContiguousContainerOOBMagic); | |
92a42be0 SL |
181 | PrintShadowByte(str, " Array cookie: ", |
182 | kAsanArrayCookieMagic); | |
183 | PrintShadowByte(str, " Intra object redzone: ", | |
184 | kAsanIntraObjectRedzone); | |
1a4d82fc | 185 | PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); |
92a42be0 SL |
186 | PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); |
187 | PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); | |
188 | } | |
189 | ||
190 | void MaybeDumpInstructionBytes(uptr pc) { | |
191 | if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) | |
192 | return; | |
193 | InternalScopedString str(1024); | |
194 | str.append("First 16 instruction bytes at pc: "); | |
195 | if (IsAccessibleMemoryRange(pc, 16)) { | |
196 | for (int i = 0; i < 16; ++i) { | |
197 | PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " "); | |
198 | } | |
199 | str.append("\n"); | |
200 | } else { | |
201 | str.append("unaccessible\n"); | |
202 | } | |
203 | Report("%s", str.data()); | |
1a4d82fc JJ |
204 | } |
205 | ||
206 | static void PrintShadowMemoryForAddress(uptr addr) { | |
207 | if (!AddrIsInMem(addr)) return; | |
208 | uptr shadow_addr = MemToShadow(addr); | |
209 | const uptr n_bytes_per_row = 16; | |
210 | uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); | |
211 | InternalScopedString str(4096 * 8); | |
212 | str.append("Shadow bytes around the buggy address:\n"); | |
213 | for (int i = -5; i <= 5; i++) { | |
214 | const char *prefix = (i == 0) ? "=>" : " "; | |
215 | PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row), | |
216 | (u8 *)shadow_addr, n_bytes_per_row); | |
217 | } | |
218 | if (flags()->print_legend) PrintLegend(&str); | |
219 | Printf("%s", str.data()); | |
220 | } | |
221 | ||
222 | static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, | |
223 | const char *zone_name) { | |
224 | if (zone_ptr) { | |
225 | if (zone_name) { | |
226 | Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", | |
227 | ptr, zone_ptr, zone_name); | |
228 | } else { | |
229 | Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", | |
230 | ptr, zone_ptr); | |
231 | } | |
232 | } else { | |
233 | Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); | |
234 | } | |
235 | } | |
236 | ||
237 | static void DescribeThread(AsanThread *t) { | |
238 | if (t) | |
239 | DescribeThread(t->context()); | |
240 | } | |
241 | ||
242 | // ---------------------- Address Descriptions ------------------- {{{1 | |
243 | ||
244 | static bool IsASCII(unsigned char c) { | |
245 | return /*0x00 <= c &&*/ c <= 0x7F; | |
246 | } | |
247 | ||
248 | static const char *MaybeDemangleGlobalName(const char *name) { | |
249 | // We can spoil names of globals with C linkage, so use an heuristic | |
250 | // approach to check if the name should be demangled. | |
92a42be0 SL |
251 | bool should_demangle = false; |
252 | if (name[0] == '_' && name[1] == 'Z') | |
253 | should_demangle = true; | |
254 | else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') | |
255 | should_demangle = true; | |
256 | ||
257 | return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; | |
1a4d82fc JJ |
258 | } |
259 | ||
260 | // Check if the global is a zero-terminated ASCII string. If so, print it. | |
261 | static void PrintGlobalNameIfASCII(InternalScopedString *str, | |
262 | const __asan_global &g) { | |
263 | for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { | |
264 | unsigned char c = *(unsigned char*)p; | |
265 | if (c == '\0' || !IsASCII(c)) return; | |
266 | } | |
267 | if (*(char*)(g.beg + g.size - 1) != '\0') return; | |
268 | str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), | |
269 | (char *)g.beg); | |
270 | } | |
271 | ||
92a42be0 SL |
272 | static const char *GlobalFilename(const __asan_global &g) { |
273 | const char *res = g.module_name; | |
274 | // Prefer the filename from source location, if is available. | |
275 | if (g.location) | |
276 | res = g.location->filename; | |
277 | CHECK(res); | |
278 | return res; | |
279 | } | |
280 | ||
281 | static void PrintGlobalLocation(InternalScopedString *str, | |
282 | const __asan_global &g) { | |
283 | str->append("%s", GlobalFilename(g)); | |
284 | if (!g.location) | |
285 | return; | |
286 | if (g.location->line_no) | |
287 | str->append(":%d", g.location->line_no); | |
288 | if (g.location->column_no) | |
289 | str->append(":%d", g.location->column_no); | |
290 | } | |
291 | ||
292 | static void DescribeAddressRelativeToGlobal(uptr addr, uptr size, | |
293 | const __asan_global &g) { | |
1a4d82fc JJ |
294 | InternalScopedString str(4096); |
295 | Decorator d; | |
296 | str.append("%s", d.Location()); | |
297 | if (addr < g.beg) { | |
298 | str.append("%p is located %zd bytes to the left", (void *)addr, | |
299 | g.beg - addr); | |
300 | } else if (addr + size > g.beg + g.size) { | |
301 | if (addr < g.beg + g.size) | |
302 | addr = g.beg + g.size; | |
303 | str.append("%p is located %zd bytes to the right", (void *)addr, | |
304 | addr - (g.beg + g.size)); | |
305 | } else { | |
306 | // Can it happen? | |
307 | str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); | |
308 | } | |
92a42be0 SL |
309 | str.append(" of global variable '%s' defined in '", |
310 | MaybeDemangleGlobalName(g.name)); | |
311 | PrintGlobalLocation(&str, g); | |
312 | str.append("' (0x%zx) of size %zu\n", g.beg, g.size); | |
1a4d82fc JJ |
313 | str.append("%s", d.EndLocation()); |
314 | PrintGlobalNameIfASCII(&str, g); | |
315 | Printf("%s", str.data()); | |
92a42be0 SL |
316 | } |
317 | ||
318 | static bool DescribeAddressIfGlobal(uptr addr, uptr size, | |
319 | const char *bug_type) { | |
320 | // Assume address is close to at most four globals. | |
321 | const int kMaxGlobalsInReport = 4; | |
322 | __asan_global globals[kMaxGlobalsInReport]; | |
323 | u32 reg_sites[kMaxGlobalsInReport]; | |
324 | int globals_num = | |
325 | GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals)); | |
326 | if (globals_num == 0) | |
327 | return false; | |
328 | for (int i = 0; i < globals_num; i++) { | |
329 | DescribeAddressRelativeToGlobal(addr, size, globals[i]); | |
330 | if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") && | |
331 | reg_sites[i]) { | |
332 | Printf(" registered at:\n"); | |
333 | StackDepotGet(reg_sites[i]).Print(); | |
334 | } | |
335 | } | |
1a4d82fc JJ |
336 | return true; |
337 | } | |
338 | ||
92a42be0 | 339 | bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) { |
1a4d82fc JJ |
340 | if (AddrIsInMem(addr)) |
341 | return false; | |
92a42be0 SL |
342 | const char *area_type = nullptr; |
343 | if (AddrIsInShadowGap(addr)) area_type = "shadow gap"; | |
344 | else if (AddrIsInHighShadow(addr)) area_type = "high shadow"; | |
345 | else if (AddrIsInLowShadow(addr)) area_type = "low shadow"; | |
346 | if (area_type != nullptr) { | |
347 | if (print) { | |
348 | Printf("Address %p is located in the %s area.\n", addr, area_type); | |
349 | } else { | |
350 | CHECK(descr); | |
351 | descr->region_kind = area_type; | |
352 | } | |
1a4d82fc JJ |
353 | return true; |
354 | } | |
355 | CHECK(0 && "Address is not in memory and not in shadow?"); | |
356 | return false; | |
357 | } | |
358 | ||
359 | // Return " (thread_name) " or an empty string if the name is empty. | |
360 | const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], | |
361 | uptr buff_len) { | |
362 | const char *name = t->name; | |
363 | if (name[0] == '\0') return ""; | |
364 | buff[0] = 0; | |
365 | internal_strncat(buff, " (", 3); | |
366 | internal_strncat(buff, name, buff_len - 4); | |
367 | internal_strncat(buff, ")", 2); | |
368 | return buff; | |
369 | } | |
370 | ||
371 | const char *ThreadNameWithParenthesis(u32 tid, char buff[], | |
372 | uptr buff_len) { | |
373 | if (tid == kInvalidTid) return ""; | |
374 | asanThreadRegistry().CheckLocked(); | |
375 | AsanThreadContext *t = GetThreadContextByTidLocked(tid); | |
376 | return ThreadNameWithParenthesis(t, buff, buff_len); | |
377 | } | |
378 | ||
92a42be0 SL |
379 | static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, |
380 | uptr access_size, uptr prev_var_end, | |
381 | uptr next_var_beg) { | |
382 | uptr var_end = var.beg + var.size; | |
1a4d82fc | 383 | uptr addr_end = addr + access_size; |
92a42be0 SL |
384 | const char *pos_descr = nullptr; |
385 | // If the variable [var.beg, var_end) is the nearest variable to the | |
1a4d82fc | 386 | // current memory access, indicate it in the log. |
92a42be0 | 387 | if (addr >= var.beg) { |
1a4d82fc JJ |
388 | if (addr_end <= var_end) |
389 | pos_descr = "is inside"; // May happen if this is a use-after-return. | |
390 | else if (addr < var_end) | |
391 | pos_descr = "partially overflows"; | |
392 | else if (addr_end <= next_var_beg && | |
393 | next_var_beg - addr_end >= addr - var_end) | |
394 | pos_descr = "overflows"; | |
395 | } else { | |
92a42be0 | 396 | if (addr_end > var.beg) |
1a4d82fc JJ |
397 | pos_descr = "partially underflows"; |
398 | else if (addr >= prev_var_end && | |
92a42be0 | 399 | addr - prev_var_end >= var.beg - addr_end) |
1a4d82fc JJ |
400 | pos_descr = "underflows"; |
401 | } | |
402 | InternalScopedString str(1024); | |
92a42be0 SL |
403 | str.append(" [%zd, %zd)", var.beg, var_end); |
404 | // Render variable name. | |
405 | str.append(" '"); | |
406 | for (uptr i = 0; i < var.name_len; ++i) { | |
407 | str.append("%c", var.name_pos[i]); | |
408 | } | |
409 | str.append("'"); | |
1a4d82fc JJ |
410 | if (pos_descr) { |
411 | Decorator d; | |
412 | // FIXME: we may want to also print the size of the access here, | |
413 | // but in case of accesses generated by memset it may be confusing. | |
414 | str.append("%s <== Memory access at offset %zd %s this variable%s\n", | |
415 | d.Location(), addr, pos_descr, d.EndLocation()); | |
416 | } else { | |
417 | str.append("\n"); | |
418 | } | |
419 | Printf("%s", str.data()); | |
420 | } | |
421 | ||
92a42be0 SL |
422 | bool ParseFrameDescription(const char *frame_descr, |
423 | InternalMmapVector<StackVarDescr> *vars) { | |
424 | CHECK(frame_descr); | |
425 | char *p; | |
426 | // This string is created by the compiler and has the following form: | |
427 | // "n alloc_1 alloc_2 ... alloc_n" | |
428 | // where alloc_i looks like "offset size len ObjectName". | |
429 | uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); | |
430 | if (n_objects == 0) | |
431 | return false; | |
432 | ||
433 | for (uptr i = 0; i < n_objects; i++) { | |
434 | uptr beg = (uptr)internal_simple_strtoll(p, &p, 10); | |
435 | uptr size = (uptr)internal_simple_strtoll(p, &p, 10); | |
436 | uptr len = (uptr)internal_simple_strtoll(p, &p, 10); | |
437 | if (beg == 0 || size == 0 || *p != ' ') { | |
438 | return false; | |
439 | } | |
440 | p++; | |
441 | StackVarDescr var = {beg, size, p, len}; | |
442 | vars->push_back(var); | |
443 | p += len; | |
444 | } | |
445 | ||
446 | return true; | |
447 | } | |
1a4d82fc JJ |
448 | |
449 | bool DescribeAddressIfStack(uptr addr, uptr access_size) { | |
450 | AsanThread *t = FindThreadByStackAddress(addr); | |
451 | if (!t) return false; | |
1a4d82fc | 452 | |
1a4d82fc | 453 | Decorator d; |
92a42be0 | 454 | char tname[128]; |
1a4d82fc | 455 | Printf("%s", d.Location()); |
92a42be0 SL |
456 | Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(), |
457 | ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname))); | |
458 | ||
459 | // Try to fetch precise stack frame for this access. | |
460 | AsanThread::StackFrameAccess access; | |
461 | if (!t->GetStackFrameAccessByAddr(addr, &access)) { | |
462 | Printf("%s\n", d.EndLocation()); | |
463 | return true; | |
464 | } | |
465 | Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation()); | |
466 | ||
1a4d82fc JJ |
467 | // Now we print the frame where the alloca has happened. |
468 | // We print this frame as a stack trace with one element. | |
469 | // The symbolizer may print more than one frame if inlining was involved. | |
470 | // The frame numbers may be different than those in the stack trace printed | |
471 | // previously. That's unfortunate, but I have no better solution, | |
472 | // especially given that the alloca may be from entirely different place | |
473 | // (e.g. use-after-scope, or different thread's stack). | |
5bcae85e | 474 | #if SANITIZER_PPC64V1 |
92a42be0 SL |
475 | // On PowerPC64 ELFv1, the address of a function actually points to a |
476 | // three-doubleword data structure with the first field containing | |
477 | // the address of the function's code. | |
478 | access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc); | |
479 | #endif | |
480 | access.frame_pc += 16; | |
1a4d82fc | 481 | Printf("%s", d.EndLocation()); |
92a42be0 | 482 | StackTrace alloca_stack(&access.frame_pc, 1); |
1a4d82fc | 483 | alloca_stack.Print(); |
92a42be0 SL |
484 | |
485 | InternalMmapVector<StackVarDescr> vars(16); | |
486 | if (!ParseFrameDescription(access.frame_descr, &vars)) { | |
487 | Printf("AddressSanitizer can't parse the stack frame " | |
488 | "descriptor: |%s|\n", access.frame_descr); | |
489 | // 'addr' is a stack address, so return true even if we can't parse frame | |
490 | return true; | |
491 | } | |
492 | uptr n_objects = vars.size(); | |
1a4d82fc | 493 | // Report the number of stack objects. |
1a4d82fc JJ |
494 | Printf(" This frame has %zu object(s):\n", n_objects); |
495 | ||
496 | // Report all objects in this frame. | |
1a4d82fc | 497 | for (uptr i = 0; i < n_objects; i++) { |
1a4d82fc JJ |
498 | uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; |
499 | uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); | |
92a42be0 | 500 | PrintAccessAndVarIntersection(vars[i], access.offset, access_size, |
1a4d82fc JJ |
501 | prev_var_end, next_var_beg); |
502 | } | |
503 | Printf("HINT: this may be a false positive if your program uses " | |
92a42be0 SL |
504 | "some custom stack unwind mechanism or swapcontext\n"); |
505 | if (SANITIZER_WINDOWS) | |
506 | Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); | |
507 | else | |
508 | Printf(" (longjmp and C++ exceptions *are* supported)\n"); | |
509 | ||
1a4d82fc JJ |
510 | DescribeThread(t); |
511 | return true; | |
512 | } | |
513 | ||
514 | static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, | |
515 | uptr access_size) { | |
516 | sptr offset; | |
517 | Decorator d; | |
518 | InternalScopedString str(4096); | |
519 | str.append("%s", d.Location()); | |
520 | if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { | |
521 | str.append("%p is located %zd bytes to the left of", (void *)addr, offset); | |
522 | } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { | |
523 | if (offset < 0) { | |
524 | addr -= offset; | |
525 | offset = 0; | |
526 | } | |
527 | str.append("%p is located %zd bytes to the right of", (void *)addr, offset); | |
528 | } else if (chunk.AddrIsInside(addr, access_size, &offset)) { | |
529 | str.append("%p is located %zd bytes inside of", (void*)addr, offset); | |
530 | } else { | |
531 | str.append("%p is located somewhere around (this is AddressSanitizer bug!)", | |
532 | (void *)addr); | |
533 | } | |
534 | str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), | |
535 | (void *)(chunk.Beg()), (void *)(chunk.End())); | |
536 | str.append("%s", d.EndLocation()); | |
537 | Printf("%s", str.data()); | |
538 | } | |
539 | ||
540 | void DescribeHeapAddress(uptr addr, uptr access_size) { | |
541 | AsanChunkView chunk = FindHeapChunkByAddress(addr); | |
542 | if (!chunk.IsValid()) { | |
543 | Printf("AddressSanitizer can not describe address in more detail " | |
544 | "(wild memory access suspected).\n"); | |
545 | return; | |
546 | } | |
547 | DescribeAccessToHeapChunk(chunk, addr, access_size); | |
548 | CHECK(chunk.AllocTid() != kInvalidTid); | |
549 | asanThreadRegistry().CheckLocked(); | |
550 | AsanThreadContext *alloc_thread = | |
551 | GetThreadContextByTidLocked(chunk.AllocTid()); | |
92a42be0 | 552 | StackTrace alloc_stack = chunk.GetAllocStack(); |
1a4d82fc JJ |
553 | char tname[128]; |
554 | Decorator d; | |
92a42be0 | 555 | AsanThreadContext *free_thread = nullptr; |
1a4d82fc JJ |
556 | if (chunk.FreeTid() != kInvalidTid) { |
557 | free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); | |
558 | Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), | |
559 | free_thread->tid, | |
560 | ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), | |
561 | d.EndAllocation()); | |
92a42be0 | 562 | StackTrace free_stack = chunk.GetFreeStack(); |
1a4d82fc JJ |
563 | free_stack.Print(); |
564 | Printf("%spreviously allocated by thread T%d%s here:%s\n", | |
565 | d.Allocation(), alloc_thread->tid, | |
566 | ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), | |
567 | d.EndAllocation()); | |
568 | } else { | |
569 | Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), | |
570 | alloc_thread->tid, | |
571 | ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), | |
572 | d.EndAllocation()); | |
573 | } | |
574 | alloc_stack.Print(); | |
575 | DescribeThread(GetCurrentThread()); | |
576 | if (free_thread) | |
577 | DescribeThread(free_thread); | |
578 | DescribeThread(alloc_thread); | |
579 | } | |
580 | ||
92a42be0 | 581 | static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) { |
1a4d82fc JJ |
582 | // Check if this is shadow or shadow gap. |
583 | if (DescribeAddressIfShadow(addr)) | |
584 | return; | |
585 | CHECK(AddrIsInMem(addr)); | |
92a42be0 | 586 | if (DescribeAddressIfGlobal(addr, access_size, bug_type)) |
1a4d82fc JJ |
587 | return; |
588 | if (DescribeAddressIfStack(addr, access_size)) | |
589 | return; | |
590 | // Assume it is a heap address. | |
591 | DescribeHeapAddress(addr, access_size); | |
592 | } | |
593 | ||
594 | // ------------------- Thread description -------------------- {{{1 | |
595 | ||
596 | void DescribeThread(AsanThreadContext *context) { | |
597 | CHECK(context); | |
598 | asanThreadRegistry().CheckLocked(); | |
599 | // No need to announce the main thread. | |
600 | if (context->tid == 0 || context->announced) { | |
601 | return; | |
602 | } | |
603 | context->announced = true; | |
604 | char tname[128]; | |
605 | InternalScopedString str(1024); | |
606 | str.append("Thread T%d%s", context->tid, | |
607 | ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); | |
92a42be0 SL |
608 | if (context->parent_tid == kInvalidTid) { |
609 | str.append(" created by unknown thread\n"); | |
610 | Printf("%s", str.data()); | |
611 | return; | |
612 | } | |
1a4d82fc JJ |
613 | str.append( |
614 | " created by T%d%s here:\n", context->parent_tid, | |
615 | ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); | |
616 | Printf("%s", str.data()); | |
92a42be0 | 617 | StackDepotGet(context->stack_id).Print(); |
1a4d82fc JJ |
618 | // Recursively described parent thread if needed. |
619 | if (flags()->print_full_thread_history) { | |
620 | AsanThreadContext *parent_context = | |
621 | GetThreadContextByTidLocked(context->parent_tid); | |
622 | DescribeThread(parent_context); | |
623 | } | |
624 | } | |
625 | ||
626 | // -------------------- Different kinds of reports ----------------- {{{1 | |
627 | ||
628 | // Use ScopedInErrorReport to run common actions just before and | |
629 | // immediately after printing error report. | |
630 | class ScopedInErrorReport { | |
631 | public: | |
3157f602 XL |
632 | explicit ScopedInErrorReport(ReportData *report = nullptr, |
633 | bool fatal = false) { | |
634 | halt_on_error_ = fatal || flags()->halt_on_error; | |
635 | ||
636 | if (lock_.TryLock()) { | |
637 | StartReporting(report); | |
638 | return; | |
639 | } | |
640 | ||
641 | // ASan found two bugs in different threads simultaneously. | |
642 | ||
643 | u32 current_tid = GetCurrentTidOrInvalid(); | |
644 | if (reporting_thread_tid_ == current_tid || | |
645 | reporting_thread_tid_ == kInvalidTid) { | |
646 | // This is either asynch signal or nested error during error reporting. | |
647 | // Fail simple to avoid deadlocks in Report(). | |
648 | ||
649 | // Can't use Report() here because of potential deadlocks | |
650 | // in nested signal handlers. | |
651 | const char msg[] = "AddressSanitizer: nested bug in the same thread, " | |
652 | "aborting.\n"; | |
653 | WriteToFile(kStderrFd, msg, sizeof(msg)); | |
654 | ||
655 | internal__exit(common_flags()->exitcode); | |
656 | } | |
657 | ||
658 | if (halt_on_error_) { | |
1a4d82fc JJ |
659 | // Do not print more than one report, otherwise they will mix up. |
660 | // Error reporting functions shouldn't return at this situation, as | |
3157f602 XL |
661 | // they are effectively no-returns. |
662 | ||
92a42be0 | 663 | Report("AddressSanitizer: while reporting a bug found another one. " |
3157f602 XL |
664 | "Ignoring.\n"); |
665 | ||
666 | // Sleep long enough to make sure that the thread which started | |
667 | // to print an error report will finish doing it. | |
668 | SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); | |
669 | ||
1a4d82fc JJ |
670 | // If we're still not dead for some reason, use raw _exit() instead of |
671 | // Die() to bypass any additional checks. | |
92a42be0 | 672 | internal__exit(common_flags()->exitcode); |
3157f602 XL |
673 | } else { |
674 | // The other thread will eventually finish reporting | |
675 | // so it's safe to wait | |
676 | lock_.Lock(); | |
677 | } | |
678 | ||
679 | StartReporting(report); | |
680 | } | |
681 | ||
682 | ~ScopedInErrorReport() { | |
683 | // Make sure the current thread is announced. | |
684 | DescribeThread(GetCurrentThread()); | |
685 | // We may want to grab this lock again when printing stats. | |
686 | asanThreadRegistry().Unlock(); | |
687 | // Print memory stats. | |
688 | if (flags()->print_stats) | |
689 | __asan_print_accumulated_stats(); | |
690 | ||
5bcae85e SL |
691 | if (common_flags()->print_cmdline) |
692 | PrintCmdline(); | |
693 | ||
3157f602 XL |
694 | // Copy the message buffer so that we could start logging without holding a |
695 | // lock that gets aquired during printing. | |
696 | InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize); | |
697 | { | |
698 | BlockingMutexLock l(&error_message_buf_mutex); | |
699 | internal_memcpy(buffer_copy.data(), | |
700 | error_message_buffer, kErrorMessageBufferSize); | |
701 | } | |
702 | ||
703 | LogFullErrorReport(buffer_copy.data()); | |
704 | ||
705 | if (error_report_callback) { | |
706 | error_report_callback(buffer_copy.data()); | |
707 | } | |
708 | CommonSanitizerReportMutex.Unlock(); | |
709 | reporting_thread_tid_ = kInvalidTid; | |
710 | lock_.Unlock(); | |
711 | if (halt_on_error_) { | |
712 | Report("ABORTING\n"); | |
713 | Die(); | |
1a4d82fc | 714 | } |
3157f602 XL |
715 | } |
716 | ||
717 | private: | |
718 | void StartReporting(ReportData *report) { | |
92a42be0 SL |
719 | if (report) report_data = *report; |
720 | report_happened = true; | |
1a4d82fc JJ |
721 | ASAN_ON_ERROR(); |
722 | // Make sure the registry and sanitizer report mutexes are locked while | |
723 | // we're printing an error report. | |
724 | // We can lock them only here to avoid self-deadlock in case of | |
725 | // recursive reports. | |
726 | asanThreadRegistry().Lock(); | |
727 | CommonSanitizerReportMutex.Lock(); | |
3157f602 | 728 | reporting_thread_tid_ = GetCurrentTidOrInvalid(); |
1a4d82fc JJ |
729 | Printf("====================================================" |
730 | "=============\n"); | |
731 | } | |
3157f602 XL |
732 | |
733 | static StaticSpinMutex lock_; | |
734 | static u32 reporting_thread_tid_; | |
735 | bool halt_on_error_; | |
1a4d82fc JJ |
736 | }; |
737 | ||
3157f602 | 738 | StaticSpinMutex ScopedInErrorReport::lock_; |
5bcae85e | 739 | u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid; |
3157f602 | 740 | |
92a42be0 | 741 | void ReportStackOverflow(const SignalContext &sig) { |
5bcae85e | 742 | ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true); |
1a4d82fc JJ |
743 | Decorator d; |
744 | Printf("%s", d.Warning()); | |
745 | Report( | |
746 | "ERROR: AddressSanitizer: stack-overflow on address %p" | |
92a42be0 SL |
747 | " (pc %p bp %p sp %p T%d)\n", |
748 | (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, | |
1a4d82fc JJ |
749 | GetCurrentTidOrInvalid()); |
750 | Printf("%s", d.EndWarning()); | |
5bcae85e | 751 | ScarinessScore::PrintSimple(10, "stack-overflow"); |
92a42be0 | 752 | GET_STACK_TRACE_SIGNAL(sig); |
1a4d82fc JJ |
753 | stack.Print(); |
754 | ReportErrorSummary("stack-overflow", &stack); | |
755 | } | |
756 | ||
92a42be0 | 757 | void ReportDeadlySignal(const char *description, const SignalContext &sig) { |
5bcae85e | 758 | ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true); |
1a4d82fc JJ |
759 | Decorator d; |
760 | Printf("%s", d.Warning()); | |
761 | Report( | |
92a42be0 SL |
762 | "ERROR: AddressSanitizer: %s on unknown address %p" |
763 | " (pc %p bp %p sp %p T%d)\n", | |
764 | description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, | |
765 | (void *)sig.sp, GetCurrentTidOrInvalid()); | |
5bcae85e SL |
766 | Printf("%s", d.EndWarning()); |
767 | ScarinessScore SS; | |
768 | if (sig.pc < GetPageSizeCached()) | |
92a42be0 | 769 | Report("Hint: pc points to the zero page.\n"); |
5bcae85e SL |
770 | if (sig.is_memory_access) { |
771 | const char *access_type = | |
772 | sig.write_flag == SignalContext::WRITE | |
773 | ? "WRITE" | |
774 | : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); | |
775 | Report("The signal is caused by a %s memory access.\n", access_type); | |
776 | if (sig.addr < GetPageSizeCached()) { | |
777 | Report("Hint: address points to the zero page.\n"); | |
778 | SS.Scare(10, "null-deref"); | |
779 | } else if (sig.addr == sig.pc) { | |
780 | SS.Scare(60, "wild-jump"); | |
781 | } else if (sig.write_flag == SignalContext::WRITE) { | |
782 | SS.Scare(30, "wild-addr-write"); | |
783 | } else if (sig.write_flag == SignalContext::READ) { | |
784 | SS.Scare(20, "wild-addr-read"); | |
785 | } else { | |
786 | SS.Scare(25, "wild-addr"); | |
787 | } | |
788 | } else { | |
789 | SS.Scare(10, "signal"); | |
92a42be0 | 790 | } |
5bcae85e | 791 | SS.Print(); |
92a42be0 | 792 | GET_STACK_TRACE_SIGNAL(sig); |
1a4d82fc | 793 | stack.Print(); |
92a42be0 | 794 | MaybeDumpInstructionBytes(sig.pc); |
1a4d82fc | 795 | Printf("AddressSanitizer can not provide additional info.\n"); |
92a42be0 | 796 | ReportErrorSummary(description, &stack); |
1a4d82fc JJ |
797 | } |
798 | ||
92a42be0 | 799 | void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { |
1a4d82fc JJ |
800 | ScopedInErrorReport in_report; |
801 | Decorator d; | |
802 | Printf("%s", d.Warning()); | |
803 | char tname[128]; | |
804 | u32 curr_tid = GetCurrentTidOrInvalid(); | |
805 | Report("ERROR: AddressSanitizer: attempting double-free on %p in " | |
806 | "thread T%d%s:\n", | |
807 | addr, curr_tid, | |
808 | ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); | |
809 | Printf("%s", d.EndWarning()); | |
810 | CHECK_GT(free_stack->size, 0); | |
5bcae85e | 811 | ScarinessScore::PrintSimple(42, "double-free"); |
1a4d82fc JJ |
812 | GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); |
813 | stack.Print(); | |
814 | DescribeHeapAddress(addr, 1); | |
815 | ReportErrorSummary("double-free", &stack); | |
816 | } | |
817 | ||
5bcae85e | 818 | void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size, |
92a42be0 SL |
819 | BufferedStackTrace *free_stack) { |
820 | ScopedInErrorReport in_report; | |
821 | Decorator d; | |
822 | Printf("%s", d.Warning()); | |
823 | char tname[128]; | |
824 | u32 curr_tid = GetCurrentTidOrInvalid(); | |
825 | Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in " | |
826 | "thread T%d%s:\n", | |
827 | addr, curr_tid, | |
828 | ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); | |
829 | Printf("%s object passed to delete has wrong type:\n", d.EndWarning()); | |
830 | Printf(" size of the allocated type: %zd bytes;\n" | |
831 | " size of the deallocated type: %zd bytes.\n", | |
5bcae85e | 832 | alloc_size, delete_size); |
92a42be0 | 833 | CHECK_GT(free_stack->size, 0); |
5bcae85e | 834 | ScarinessScore::PrintSimple(10, "new-delete-type-mismatch"); |
92a42be0 SL |
835 | GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); |
836 | stack.Print(); | |
837 | DescribeHeapAddress(addr, 1); | |
838 | ReportErrorSummary("new-delete-type-mismatch", &stack); | |
3157f602 | 839 | Report("HINT: if you don't care about these errors you may set " |
92a42be0 SL |
840 | "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); |
841 | } | |
842 | ||
843 | void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { | |
1a4d82fc JJ |
844 | ScopedInErrorReport in_report; |
845 | Decorator d; | |
846 | Printf("%s", d.Warning()); | |
847 | char tname[128]; | |
848 | u32 curr_tid = GetCurrentTidOrInvalid(); | |
849 | Report("ERROR: AddressSanitizer: attempting free on address " | |
850 | "which was not malloc()-ed: %p in thread T%d%s\n", addr, | |
851 | curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); | |
852 | Printf("%s", d.EndWarning()); | |
853 | CHECK_GT(free_stack->size, 0); | |
5bcae85e | 854 | ScarinessScore::PrintSimple(40, "bad-free"); |
1a4d82fc JJ |
855 | GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); |
856 | stack.Print(); | |
857 | DescribeHeapAddress(addr, 1); | |
858 | ReportErrorSummary("bad-free", &stack); | |
859 | } | |
860 | ||
92a42be0 | 861 | void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, |
1a4d82fc JJ |
862 | AllocType alloc_type, |
863 | AllocType dealloc_type) { | |
864 | static const char *alloc_names[] = | |
865 | {"INVALID", "malloc", "operator new", "operator new []"}; | |
866 | static const char *dealloc_names[] = | |
867 | {"INVALID", "free", "operator delete", "operator delete []"}; | |
868 | CHECK_NE(alloc_type, dealloc_type); | |
869 | ScopedInErrorReport in_report; | |
870 | Decorator d; | |
871 | Printf("%s", d.Warning()); | |
872 | Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", | |
873 | alloc_names[alloc_type], dealloc_names[dealloc_type], addr); | |
874 | Printf("%s", d.EndWarning()); | |
875 | CHECK_GT(free_stack->size, 0); | |
5bcae85e | 876 | ScarinessScore::PrintSimple(10, "alloc-dealloc-mismatch"); |
1a4d82fc JJ |
877 | GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); |
878 | stack.Print(); | |
879 | DescribeHeapAddress(addr, 1); | |
880 | ReportErrorSummary("alloc-dealloc-mismatch", &stack); | |
3157f602 | 881 | Report("HINT: if you don't care about these errors you may set " |
1a4d82fc JJ |
882 | "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); |
883 | } | |
884 | ||
92a42be0 | 885 | void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { |
1a4d82fc JJ |
886 | ScopedInErrorReport in_report; |
887 | Decorator d; | |
888 | Printf("%s", d.Warning()); | |
889 | Report("ERROR: AddressSanitizer: attempting to call " | |
890 | "malloc_usable_size() for pointer which is " | |
891 | "not owned: %p\n", addr); | |
892 | Printf("%s", d.EndWarning()); | |
893 | stack->Print(); | |
894 | DescribeHeapAddress(addr, 1); | |
895 | ReportErrorSummary("bad-malloc_usable_size", stack); | |
896 | } | |
897 | ||
92a42be0 SL |
898 | void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, |
899 | BufferedStackTrace *stack) { | |
1a4d82fc JJ |
900 | ScopedInErrorReport in_report; |
901 | Decorator d; | |
902 | Printf("%s", d.Warning()); | |
903 | Report("ERROR: AddressSanitizer: attempting to call " | |
92a42be0 | 904 | "__sanitizer_get_allocated_size() for pointer which is " |
1a4d82fc JJ |
905 | "not owned: %p\n", addr); |
906 | Printf("%s", d.EndWarning()); | |
907 | stack->Print(); | |
908 | DescribeHeapAddress(addr, 1); | |
92a42be0 | 909 | ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack); |
1a4d82fc JJ |
910 | } |
911 | ||
92a42be0 SL |
912 | void ReportStringFunctionMemoryRangesOverlap(const char *function, |
913 | const char *offset1, uptr length1, | |
914 | const char *offset2, uptr length2, | |
915 | BufferedStackTrace *stack) { | |
1a4d82fc JJ |
916 | ScopedInErrorReport in_report; |
917 | Decorator d; | |
918 | char bug_type[100]; | |
919 | internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); | |
920 | Printf("%s", d.Warning()); | |
921 | Report("ERROR: AddressSanitizer: %s: " | |
922 | "memory ranges [%p,%p) and [%p, %p) overlap\n", \ | |
923 | bug_type, offset1, offset1 + length1, offset2, offset2 + length2); | |
924 | Printf("%s", d.EndWarning()); | |
5bcae85e | 925 | ScarinessScore::PrintSimple(10, bug_type); |
1a4d82fc | 926 | stack->Print(); |
92a42be0 SL |
927 | DescribeAddress((uptr)offset1, length1, bug_type); |
928 | DescribeAddress((uptr)offset2, length2, bug_type); | |
1a4d82fc JJ |
929 | ReportErrorSummary(bug_type, stack); |
930 | } | |
931 | ||
932 | void ReportStringFunctionSizeOverflow(uptr offset, uptr size, | |
92a42be0 | 933 | BufferedStackTrace *stack) { |
1a4d82fc JJ |
934 | ScopedInErrorReport in_report; |
935 | Decorator d; | |
936 | const char *bug_type = "negative-size-param"; | |
937 | Printf("%s", d.Warning()); | |
938 | Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size); | |
939 | Printf("%s", d.EndWarning()); | |
5bcae85e | 940 | ScarinessScore::PrintSimple(10, bug_type); |
1a4d82fc | 941 | stack->Print(); |
92a42be0 | 942 | DescribeAddress(offset, size, bug_type); |
1a4d82fc JJ |
943 | ReportErrorSummary(bug_type, stack); |
944 | } | |
945 | ||
946 | void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, | |
947 | uptr old_mid, uptr new_mid, | |
92a42be0 | 948 | BufferedStackTrace *stack) { |
1a4d82fc JJ |
949 | ScopedInErrorReport in_report; |
950 | Report("ERROR: AddressSanitizer: bad parameters to " | |
951 | "__sanitizer_annotate_contiguous_container:\n" | |
952 | " beg : %p\n" | |
953 | " end : %p\n" | |
954 | " old_mid : %p\n" | |
955 | " new_mid : %p\n", | |
956 | beg, end, old_mid, new_mid); | |
92a42be0 SL |
957 | uptr granularity = SHADOW_GRANULARITY; |
958 | if (!IsAligned(beg, granularity)) | |
959 | Report("ERROR: beg is not aligned by %d\n", granularity); | |
1a4d82fc JJ |
960 | stack->Print(); |
961 | ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); | |
962 | } | |
963 | ||
92a42be0 SL |
964 | void ReportODRViolation(const __asan_global *g1, u32 stack_id1, |
965 | const __asan_global *g2, u32 stack_id2) { | |
1a4d82fc JJ |
966 | ScopedInErrorReport in_report; |
967 | Decorator d; | |
968 | Printf("%s", d.Warning()); | |
969 | Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); | |
970 | Printf("%s", d.EndWarning()); | |
92a42be0 SL |
971 | InternalScopedString g1_loc(256), g2_loc(256); |
972 | PrintGlobalLocation(&g1_loc, *g1); | |
973 | PrintGlobalLocation(&g2_loc, *g2); | |
974 | Printf(" [1] size=%zd '%s' %s\n", g1->size, | |
975 | MaybeDemangleGlobalName(g1->name), g1_loc.data()); | |
976 | Printf(" [2] size=%zd '%s' %s\n", g2->size, | |
977 | MaybeDemangleGlobalName(g2->name), g2_loc.data()); | |
978 | if (stack_id1 && stack_id2) { | |
979 | Printf("These globals were registered at these points:\n"); | |
980 | Printf(" [1]:\n"); | |
981 | StackDepotGet(stack_id1).Print(); | |
982 | Printf(" [2]:\n"); | |
983 | StackDepotGet(stack_id2).Print(); | |
984 | } | |
3157f602 | 985 | Report("HINT: if you don't care about these errors you may set " |
1a4d82fc | 986 | "ASAN_OPTIONS=detect_odr_violation=0\n"); |
92a42be0 SL |
987 | InternalScopedString error_msg(256); |
988 | error_msg.append("odr-violation: global '%s' at %s", | |
989 | MaybeDemangleGlobalName(g1->name), g1_loc.data()); | |
990 | ReportErrorSummary(error_msg.data()); | |
1a4d82fc JJ |
991 | } |
992 | ||
993 | // ----------------------- CheckForInvalidPointerPair ----------- {{{1 | |
994 | static NOINLINE void | |
995 | ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) { | |
996 | ScopedInErrorReport in_report; | |
92a42be0 | 997 | const char *bug_type = "invalid-pointer-pair"; |
1a4d82fc JJ |
998 | Decorator d; |
999 | Printf("%s", d.Warning()); | |
1000 | Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2); | |
1001 | Printf("%s", d.EndWarning()); | |
1002 | GET_STACK_TRACE_FATAL(pc, bp); | |
1003 | stack.Print(); | |
92a42be0 SL |
1004 | DescribeAddress(a1, 1, bug_type); |
1005 | DescribeAddress(a2, 1, bug_type); | |
1006 | ReportErrorSummary(bug_type, &stack); | |
1a4d82fc JJ |
1007 | } |
1008 | ||
1009 | static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { | |
1010 | if (!flags()->detect_invalid_pointer_pairs) return; | |
1011 | uptr a1 = reinterpret_cast<uptr>(p1); | |
1012 | uptr a2 = reinterpret_cast<uptr>(p2); | |
1013 | AsanChunkView chunk1 = FindHeapChunkByAddress(a1); | |
1014 | AsanChunkView chunk2 = FindHeapChunkByAddress(a2); | |
5bcae85e SL |
1015 | bool valid1 = chunk1.IsAllocated(); |
1016 | bool valid2 = chunk2.IsAllocated(); | |
1017 | if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) { | |
1018 | GET_CALLER_PC_BP_SP; | |
1a4d82fc JJ |
1019 | return ReportInvalidPointerPair(pc, bp, sp, a1, a2); |
1020 | } | |
1021 | } | |
1022 | // ----------------------- Mac-specific reports ----------------- {{{1 | |
1023 | ||
92a42be0 SL |
1024 | void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, |
1025 | BufferedStackTrace *stack) { | |
1a4d82fc JJ |
1026 | ScopedInErrorReport in_report; |
1027 | Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" | |
1028 | "This is an unrecoverable problem, exiting now.\n", | |
1029 | addr); | |
1030 | PrintZoneForPointer(addr, zone_ptr, zone_name); | |
1031 | stack->Print(); | |
1032 | DescribeHeapAddress(addr, 1); | |
1033 | } | |
1034 | ||
3157f602 XL |
1035 | // -------------- SuppressErrorReport -------------- {{{1 |
1036 | // Avoid error reports duplicating for ASan recover mode. | |
1037 | static bool SuppressErrorReport(uptr pc) { | |
1038 | if (!common_flags()->suppress_equal_pcs) return false; | |
1039 | for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { | |
1040 | uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]); | |
1041 | if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp, | |
1042 | pc, memory_order_relaxed)) | |
1043 | return false; | |
1044 | if (cmp == pc) return true; | |
1045 | } | |
1046 | Die(); | |
1a4d82fc JJ |
1047 | } |
1048 | ||
5bcae85e SL |
1049 | static void PrintContainerOverflowHint() { |
1050 | Printf("HINT: if you don't care about these errors you may set " | |
1051 | "ASAN_OPTIONS=detect_container_overflow=0.\n" | |
1052 | "If you suspect a false positive see also: " | |
1053 | "https://github.com/google/sanitizers/wiki/" | |
1054 | "AddressSanitizerContainerOverflow.\n"); | |
1055 | } | |
1056 | ||
1057 | static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) { | |
1058 | return s[-1] > 127 && s[1] > 127; | |
1059 | } | |
1060 | ||
3157f602 XL |
1061 | void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, |
1062 | uptr access_size, u32 exp, bool fatal) { | |
1063 | if (!fatal && SuppressErrorReport(pc)) return; | |
92a42be0 | 1064 | ENABLE_FRAME_POINTER; |
5bcae85e SL |
1065 | ScarinessScore SS; |
1066 | ||
1067 | if (access_size) { | |
1068 | if (access_size <= 9) { | |
1069 | char desr[] = "?-byte"; | |
1070 | desr[0] = '0' + access_size; | |
1071 | SS.Scare(access_size + access_size / 2, desr); | |
1072 | } else if (access_size >= 10) { | |
1073 | SS.Scare(15, "multi-byte"); | |
1074 | } | |
1075 | is_write ? SS.Scare(20, "write") : SS.Scare(1, "read"); | |
1076 | } | |
92a42be0 SL |
1077 | |
1078 | // Optimization experiments. | |
1079 | // The experiments can be used to evaluate potential optimizations that remove | |
1080 | // instrumentation (assess false negatives). Instead of completely removing | |
1081 | // some instrumentation, compiler can emit special calls into runtime | |
1082 | // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass | |
1083 | // mask of experiments (exp). | |
1084 | // The reaction to a non-zero value of exp is to be defined. | |
1085 | (void)exp; | |
1a4d82fc JJ |
1086 | |
1087 | // Determine the error type. | |
1088 | const char *bug_descr = "unknown-crash"; | |
5bcae85e | 1089 | u8 shadow_val = 0; |
1a4d82fc JJ |
1090 | if (AddrIsInMem(addr)) { |
1091 | u8 *shadow_addr = (u8*)MemToShadow(addr); | |
1092 | // If we are accessing 16 bytes, look at the second shadow byte. | |
1093 | if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) | |
1094 | shadow_addr++; | |
1095 | // If we are in the partial right redzone, look at the next shadow byte. | |
1096 | if (*shadow_addr > 0 && *shadow_addr < 128) | |
1097 | shadow_addr++; | |
5bcae85e SL |
1098 | bool far_from_bounds = false; |
1099 | shadow_val = *shadow_addr; | |
1100 | int bug_type_score = 0; | |
1101 | // For use-after-frees reads are almost as bad as writes. | |
1102 | int read_after_free_bonus = 0; | |
1103 | switch (shadow_val) { | |
1a4d82fc JJ |
1104 | case kAsanHeapLeftRedzoneMagic: |
1105 | case kAsanHeapRightRedzoneMagic: | |
92a42be0 | 1106 | case kAsanArrayCookieMagic: |
1a4d82fc | 1107 | bug_descr = "heap-buffer-overflow"; |
5bcae85e SL |
1108 | bug_type_score = 10; |
1109 | far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); | |
1a4d82fc JJ |
1110 | break; |
1111 | case kAsanHeapFreeMagic: | |
1112 | bug_descr = "heap-use-after-free"; | |
5bcae85e SL |
1113 | bug_type_score = 20; |
1114 | if (!is_write) read_after_free_bonus = 18; | |
1a4d82fc JJ |
1115 | break; |
1116 | case kAsanStackLeftRedzoneMagic: | |
1117 | bug_descr = "stack-buffer-underflow"; | |
5bcae85e SL |
1118 | bug_type_score = 25; |
1119 | far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); | |
1a4d82fc JJ |
1120 | break; |
1121 | case kAsanInitializationOrderMagic: | |
1122 | bug_descr = "initialization-order-fiasco"; | |
5bcae85e | 1123 | bug_type_score = 1; |
1a4d82fc JJ |
1124 | break; |
1125 | case kAsanStackMidRedzoneMagic: | |
1126 | case kAsanStackRightRedzoneMagic: | |
1127 | case kAsanStackPartialRedzoneMagic: | |
1128 | bug_descr = "stack-buffer-overflow"; | |
5bcae85e SL |
1129 | bug_type_score = 25; |
1130 | far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); | |
1a4d82fc JJ |
1131 | break; |
1132 | case kAsanStackAfterReturnMagic: | |
1133 | bug_descr = "stack-use-after-return"; | |
5bcae85e SL |
1134 | bug_type_score = 30; |
1135 | if (!is_write) read_after_free_bonus = 18; | |
1a4d82fc JJ |
1136 | break; |
1137 | case kAsanUserPoisonedMemoryMagic: | |
1138 | bug_descr = "use-after-poison"; | |
5bcae85e | 1139 | bug_type_score = 20; |
1a4d82fc JJ |
1140 | break; |
1141 | case kAsanContiguousContainerOOBMagic: | |
1142 | bug_descr = "container-overflow"; | |
5bcae85e | 1143 | bug_type_score = 10; |
1a4d82fc JJ |
1144 | break; |
1145 | case kAsanStackUseAfterScopeMagic: | |
1146 | bug_descr = "stack-use-after-scope"; | |
5bcae85e | 1147 | bug_type_score = 10; |
1a4d82fc JJ |
1148 | break; |
1149 | case kAsanGlobalRedzoneMagic: | |
1150 | bug_descr = "global-buffer-overflow"; | |
5bcae85e SL |
1151 | bug_type_score = 10; |
1152 | far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); | |
1a4d82fc | 1153 | break; |
92a42be0 SL |
1154 | case kAsanIntraObjectRedzone: |
1155 | bug_descr = "intra-object-overflow"; | |
5bcae85e | 1156 | bug_type_score = 10; |
92a42be0 SL |
1157 | break; |
1158 | case kAsanAllocaLeftMagic: | |
1159 | case kAsanAllocaRightMagic: | |
1160 | bug_descr = "dynamic-stack-buffer-overflow"; | |
5bcae85e SL |
1161 | bug_type_score = 25; |
1162 | far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); | |
92a42be0 | 1163 | break; |
1a4d82fc | 1164 | } |
5bcae85e SL |
1165 | SS.Scare(bug_type_score + read_after_free_bonus, bug_descr); |
1166 | if (far_from_bounds) | |
1167 | SS.Scare(10, "far-from-bounds"); | |
1a4d82fc | 1168 | } |
92a42be0 SL |
1169 | |
1170 | ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, | |
1171 | bug_descr }; | |
3157f602 | 1172 | ScopedInErrorReport in_report(&report, fatal); |
92a42be0 | 1173 | |
1a4d82fc JJ |
1174 | Decorator d; |
1175 | Printf("%s", d.Warning()); | |
1176 | Report("ERROR: AddressSanitizer: %s on address " | |
92a42be0 | 1177 | "%p at pc %p bp %p sp %p\n", |
1a4d82fc JJ |
1178 | bug_descr, (void*)addr, pc, bp, sp); |
1179 | Printf("%s", d.EndWarning()); | |
1180 | ||
1181 | u32 curr_tid = GetCurrentTidOrInvalid(); | |
1182 | char tname[128]; | |
1183 | Printf("%s%s of size %zu at %p thread T%d%s%s\n", | |
1184 | d.Access(), | |
1185 | access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", | |
1186 | access_size, (void*)addr, curr_tid, | |
1187 | ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), | |
1188 | d.EndAccess()); | |
1189 | ||
5bcae85e | 1190 | SS.Print(); |
1a4d82fc JJ |
1191 | GET_STACK_TRACE_FATAL(pc, bp); |
1192 | stack.Print(); | |
1193 | ||
92a42be0 | 1194 | DescribeAddress(addr, access_size, bug_descr); |
5bcae85e SL |
1195 | if (shadow_val == kAsanContiguousContainerOOBMagic) |
1196 | PrintContainerOverflowHint(); | |
1a4d82fc JJ |
1197 | ReportErrorSummary(bug_descr, &stack); |
1198 | PrintShadowMemoryForAddress(addr); | |
1199 | } | |
1200 | ||
3157f602 XL |
1201 | } // namespace __asan |
1202 | ||
1203 | // --------------------------- Interface --------------------- {{{1 | |
1204 | using namespace __asan; // NOLINT | |
1205 | ||
1206 | void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, | |
1207 | uptr access_size, u32 exp) { | |
1208 | ENABLE_FRAME_POINTER; | |
1209 | bool fatal = flags()->halt_on_error; | |
1210 | ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); | |
1211 | } | |
1212 | ||
1a4d82fc | 1213 | void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { |
3157f602 | 1214 | BlockingMutexLock l(&error_message_buf_mutex); |
1a4d82fc | 1215 | error_report_callback = callback; |
1a4d82fc JJ |
1216 | } |
1217 | ||
1218 | void __asan_describe_address(uptr addr) { | |
92a42be0 SL |
1219 | // Thread registry must be locked while we're describing an address. |
1220 | asanThreadRegistry().Lock(); | |
1221 | DescribeAddress(addr, 1, ""); | |
1222 | asanThreadRegistry().Unlock(); | |
1223 | } | |
1224 | ||
1225 | int __asan_report_present() { | |
1226 | return report_happened ? 1 : 0; | |
1227 | } | |
1228 | ||
1229 | uptr __asan_get_report_pc() { | |
1230 | return report_data.pc; | |
1231 | } | |
1232 | ||
1233 | uptr __asan_get_report_bp() { | |
1234 | return report_data.bp; | |
1235 | } | |
1236 | ||
1237 | uptr __asan_get_report_sp() { | |
1238 | return report_data.sp; | |
1239 | } | |
1240 | ||
1241 | uptr __asan_get_report_address() { | |
1242 | return report_data.addr; | |
1243 | } | |
1244 | ||
1245 | int __asan_get_report_access_type() { | |
1246 | return report_data.is_write ? 1 : 0; | |
1247 | } | |
1248 | ||
1249 | uptr __asan_get_report_access_size() { | |
1250 | return report_data.access_size; | |
1251 | } | |
1252 | ||
1253 | const char *__asan_get_report_description() { | |
1254 | return report_data.description; | |
1a4d82fc JJ |
1255 | } |
1256 | ||
1257 | extern "C" { | |
1258 | SANITIZER_INTERFACE_ATTRIBUTE | |
1259 | void __sanitizer_ptr_sub(void *a, void *b) { | |
1260 | CheckForInvalidPointerPair(a, b); | |
1261 | } | |
1262 | SANITIZER_INTERFACE_ATTRIBUTE | |
1263 | void __sanitizer_ptr_cmp(void *a, void *b) { | |
1264 | CheckForInvalidPointerPair(a, b); | |
1265 | } | |
92a42be0 | 1266 | } // extern "C" |
1a4d82fc JJ |
1267 | |
1268 | #if !SANITIZER_SUPPORTS_WEAK_HOOKS | |
1269 | // Provide default implementation of __asan_on_error that does nothing | |
1270 | // and may be overriden by user. | |
1271 | SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE | |
1272 | void __asan_on_error() {} | |
1273 | #endif |