1 | //===-- asan_report.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 AddressSanitizer, an address sanity checker. |
10 | // |
11 | // This file contains error reporting code. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "asan_report.h" |
15 | |
16 | #include "asan_descriptions.h" |
17 | #include "asan_errors.h" |
18 | #include "asan_flags.h" |
19 | #include "asan_internal.h" |
20 | #include "asan_mapping.h" |
21 | #include "asan_scariness_score.h" |
22 | #include "asan_stack.h" |
23 | #include "asan_thread.h" |
24 | #include "sanitizer_common/sanitizer_common.h" |
25 | #include "sanitizer_common/sanitizer_flags.h" |
26 | #include "sanitizer_common/sanitizer_interface_internal.h" |
27 | #include "sanitizer_common/sanitizer_placement_new.h" |
28 | #include "sanitizer_common/sanitizer_report_decorator.h" |
29 | #include "sanitizer_common/sanitizer_stackdepot.h" |
30 | #include "sanitizer_common/sanitizer_symbolizer.h" |
31 | |
32 | namespace __asan { |
33 | |
34 | // -------------------- User-specified callbacks ----------------- {{{1 |
35 | static void (*error_report_callback)(const char*); |
36 | using ErrorMessageBuffer = InternalMmapVectorNoCtor<char, true>; |
37 | alignas( |
38 | alignof(ErrorMessageBuffer)) static char error_message_buffer_placeholder |
39 | [sizeof(ErrorMessageBuffer)]; |
40 | static ErrorMessageBuffer *error_message_buffer = nullptr; |
41 | static Mutex error_message_buf_mutex; |
42 | static const unsigned kAsanBuggyPcPoolSize = 25; |
43 | static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; |
44 | |
45 | void AppendToErrorMessageBuffer(const char *buffer) { |
46 | Lock l(&error_message_buf_mutex); |
47 | if (!error_message_buffer) { |
48 | error_message_buffer = |
49 | new (error_message_buffer_placeholder) ErrorMessageBuffer(); |
50 | error_message_buffer->Initialize(initial_capacity: kErrorMessageBufferSize); |
51 | } |
52 | uptr error_message_buffer_len = error_message_buffer->size(); |
53 | uptr buffer_len = internal_strlen(s: buffer); |
54 | error_message_buffer->resize(new_size: error_message_buffer_len + buffer_len); |
55 | internal_memcpy(dest: error_message_buffer->data() + error_message_buffer_len, |
56 | src: buffer, n: buffer_len); |
57 | } |
58 | |
59 | // ---------------------- Helper functions ----------------------- {{{1 |
60 | |
61 | void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, |
62 | bool in_shadow, const char *after) { |
63 | Decorator d; |
64 | str->AppendF(format: "%s%s%x%x%s%s" , before, |
65 | in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4, |
66 | byte & 15, d.Default(), after); |
67 | } |
68 | |
69 | static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, |
70 | const char *zone_name) { |
71 | if (zone_ptr) { |
72 | if (zone_name) { |
73 | Printf(format: "malloc_zone_from_ptr(%p) = %p, which is %s\n" , (void *)ptr, |
74 | (void *)zone_ptr, zone_name); |
75 | } else { |
76 | Printf(format: "malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n" , |
77 | (void *)ptr, (void *)zone_ptr); |
78 | } |
79 | } else { |
80 | Printf(format: "malloc_zone_from_ptr(%p) = 0\n" , (void *)ptr); |
81 | } |
82 | } |
83 | |
84 | // ---------------------- Address Descriptions ------------------- {{{1 |
85 | |
86 | bool ParseFrameDescription(const char *frame_descr, |
87 | InternalMmapVector<StackVarDescr> *vars) { |
88 | CHECK(frame_descr); |
89 | const char *p; |
90 | // This string is created by the compiler and has the following form: |
91 | // "n alloc_1 alloc_2 ... alloc_n" |
92 | // where alloc_i looks like "offset size len ObjectName" |
93 | // or "offset size len ObjectName:line". |
94 | uptr n_objects = (uptr)internal_simple_strtoll(nptr: frame_descr, endptr: &p, base: 10); |
95 | if (n_objects == 0) |
96 | return false; |
97 | |
98 | for (uptr i = 0; i < n_objects; i++) { |
99 | uptr beg = (uptr)internal_simple_strtoll(nptr: p, endptr: &p, base: 10); |
100 | uptr size = (uptr)internal_simple_strtoll(nptr: p, endptr: &p, base: 10); |
101 | uptr len = (uptr)internal_simple_strtoll(nptr: p, endptr: &p, base: 10); |
102 | if (beg == 0 || size == 0 || *p != ' ') { |
103 | return false; |
104 | } |
105 | p++; |
106 | char *colon_pos = internal_strchr(s: p, c: ':'); |
107 | uptr line = 0; |
108 | uptr name_len = len; |
109 | if (colon_pos != nullptr && colon_pos < p + len) { |
110 | name_len = colon_pos - p; |
111 | line = (uptr)internal_simple_strtoll(nptr: colon_pos + 1, endptr: nullptr, base: 10); |
112 | } |
113 | StackVarDescr var = {.beg: beg, .size: size, .name_pos: p, .name_len: name_len, .line: line}; |
114 | vars->push_back(element: var); |
115 | p += len; |
116 | } |
117 | |
118 | return true; |
119 | } |
120 | |
121 | // -------------------- Different kinds of reports ----------------- {{{1 |
122 | |
123 | // Use ScopedInErrorReport to run common actions just before and |
124 | // immediately after printing error report. |
125 | class ScopedInErrorReport { |
126 | public: |
127 | explicit ScopedInErrorReport(bool fatal = false) |
128 | : halt_on_error_(fatal || flags()->halt_on_error) { |
129 | // Make sure the registry and sanitizer report mutexes are locked while |
130 | // we're printing an error report. |
131 | // We can lock them only here to avoid self-deadlock in case of |
132 | // recursive reports. |
133 | asanThreadRegistry().Lock(); |
134 | Printf( |
135 | format: "=================================================================\n" ); |
136 | } |
137 | |
138 | ~ScopedInErrorReport() { |
139 | if (halt_on_error_ && !__sanitizer_acquire_crash_state()) { |
140 | asanThreadRegistry().Unlock(); |
141 | return; |
142 | } |
143 | ASAN_ON_ERROR(); |
144 | if (current_error_.IsValid()) current_error_.Print(); |
145 | |
146 | // Make sure the current thread is announced. |
147 | DescribeThread(t: GetCurrentThread()); |
148 | // We may want to grab this lock again when printing stats. |
149 | asanThreadRegistry().Unlock(); |
150 | // Print memory stats. |
151 | if (flags()->print_stats) |
152 | __asan_print_accumulated_stats(); |
153 | |
154 | if (common_flags()->print_cmdline) |
155 | PrintCmdline(); |
156 | |
157 | if (common_flags()->print_module_map == 2) |
158 | DumpProcessMap(); |
159 | |
160 | // Copy the message buffer so that we could start logging without holding a |
161 | // lock that gets acquired during printing. |
162 | InternalScopedString buffer_copy; |
163 | { |
164 | Lock l(&error_message_buf_mutex); |
165 | error_message_buffer->push_back(element: '\0'); |
166 | buffer_copy.Append(str: error_message_buffer->data()); |
167 | // Clear error_message_buffer so that if we find other errors |
168 | // we don't re-log this error. |
169 | error_message_buffer->clear(); |
170 | } |
171 | |
172 | LogFullErrorReport(buffer: buffer_copy.data()); |
173 | |
174 | if (error_report_callback) { |
175 | error_report_callback(buffer_copy.data()); |
176 | } |
177 | |
178 | if (halt_on_error_ && common_flags()->abort_on_error) { |
179 | // On Android the message is truncated to 512 characters. |
180 | // FIXME: implement "compact" error format, possibly without, or with |
181 | // highly compressed stack traces? |
182 | // FIXME: or just use the summary line as abort message? |
183 | SetAbortMessage(buffer_copy.data()); |
184 | } |
185 | |
186 | // In halt_on_error = false mode, reset the current error object (before |
187 | // unlocking). |
188 | if (!halt_on_error_) |
189 | internal_memset(s: ¤t_error_, c: 0, n: sizeof(current_error_)); |
190 | |
191 | if (halt_on_error_) { |
192 | Report(format: "ABORTING\n" ); |
193 | Die(); |
194 | } |
195 | } |
196 | |
197 | void ReportError(const ErrorDescription &description) { |
198 | // Can only report one error per ScopedInErrorReport. |
199 | CHECK_EQ(current_error_.kind, kErrorKindInvalid); |
200 | internal_memcpy(dest: ¤t_error_, src: &description, n: sizeof(current_error_)); |
201 | } |
202 | |
203 | static ErrorDescription &CurrentError() { |
204 | return current_error_; |
205 | } |
206 | |
207 | private: |
208 | ScopedErrorReportLock error_report_lock_; |
209 | // Error currently being reported. This enables the destructor to interact |
210 | // with the debugger and point it to an error description. |
211 | static ErrorDescription current_error_; |
212 | bool halt_on_error_; |
213 | }; |
214 | |
215 | ErrorDescription ScopedInErrorReport::current_error_(LINKER_INITIALIZED); |
216 | |
217 | void ReportDeadlySignal(const SignalContext &sig) { |
218 | ScopedInErrorReport in_report(/*fatal*/ true); |
219 | ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig); |
220 | in_report.ReportError(description: error); |
221 | } |
222 | |
223 | void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { |
224 | ScopedInErrorReport in_report; |
225 | ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr); |
226 | in_report.ReportError(description: error); |
227 | } |
228 | |
229 | void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size, |
230 | uptr delete_alignment, |
231 | BufferedStackTrace *free_stack) { |
232 | ScopedInErrorReport in_report; |
233 | ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, |
234 | delete_size, delete_alignment); |
235 | in_report.ReportError(description: error); |
236 | } |
237 | |
238 | void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { |
239 | ScopedInErrorReport in_report; |
240 | ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr); |
241 | in_report.ReportError(description: error); |
242 | } |
243 | |
244 | void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, |
245 | AllocType alloc_type, |
246 | AllocType dealloc_type) { |
247 | ScopedInErrorReport in_report; |
248 | ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, |
249 | alloc_type, dealloc_type); |
250 | in_report.ReportError(description: error); |
251 | } |
252 | |
253 | void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { |
254 | ScopedInErrorReport in_report; |
255 | ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr); |
256 | in_report.ReportError(description: error); |
257 | } |
258 | |
259 | void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, |
260 | BufferedStackTrace *stack) { |
261 | ScopedInErrorReport in_report; |
262 | ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack, |
263 | addr); |
264 | in_report.ReportError(description: error); |
265 | } |
266 | |
267 | void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack) { |
268 | ScopedInErrorReport in_report(/*fatal*/ true); |
269 | ErrorCallocOverflow error(GetCurrentTidOrInvalid(), stack, count, size); |
270 | in_report.ReportError(description: error); |
271 | } |
272 | |
273 | void ReportReallocArrayOverflow(uptr count, uptr size, |
274 | BufferedStackTrace *stack) { |
275 | ScopedInErrorReport in_report(/*fatal*/ true); |
276 | ErrorReallocArrayOverflow error(GetCurrentTidOrInvalid(), stack, count, size); |
277 | in_report.ReportError(description: error); |
278 | } |
279 | |
280 | void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack) { |
281 | ScopedInErrorReport in_report(/*fatal*/ true); |
282 | ErrorPvallocOverflow error(GetCurrentTidOrInvalid(), stack, size); |
283 | in_report.ReportError(description: error); |
284 | } |
285 | |
286 | void ReportInvalidAllocationAlignment(uptr alignment, |
287 | BufferedStackTrace *stack) { |
288 | ScopedInErrorReport in_report(/*fatal*/ true); |
289 | ErrorInvalidAllocationAlignment error(GetCurrentTidOrInvalid(), stack, |
290 | alignment); |
291 | in_report.ReportError(description: error); |
292 | } |
293 | |
294 | void ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment, |
295 | BufferedStackTrace *stack) { |
296 | ScopedInErrorReport in_report(/*fatal*/ true); |
297 | ErrorInvalidAlignedAllocAlignment error(GetCurrentTidOrInvalid(), stack, |
298 | size, alignment); |
299 | in_report.ReportError(description: error); |
300 | } |
301 | |
302 | void ReportInvalidPosixMemalignAlignment(uptr alignment, |
303 | BufferedStackTrace *stack) { |
304 | ScopedInErrorReport in_report(/*fatal*/ true); |
305 | ErrorInvalidPosixMemalignAlignment error(GetCurrentTidOrInvalid(), stack, |
306 | alignment); |
307 | in_report.ReportError(description: error); |
308 | } |
309 | |
310 | void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size, |
311 | BufferedStackTrace *stack) { |
312 | ScopedInErrorReport in_report(/*fatal*/ true); |
313 | ErrorAllocationSizeTooBig error(GetCurrentTidOrInvalid(), stack, user_size, |
314 | total_size, max_size); |
315 | in_report.ReportError(description: error); |
316 | } |
317 | |
318 | void (BufferedStackTrace *stack) { |
319 | ScopedInErrorReport in_report(/*fatal*/ true); |
320 | ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack); |
321 | in_report.ReportError(description: error); |
322 | } |
323 | |
324 | void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack) { |
325 | ScopedInErrorReport in_report(/*fatal*/ true); |
326 | ErrorOutOfMemory error(GetCurrentTidOrInvalid(), stack, requested_size); |
327 | in_report.ReportError(description: error); |
328 | } |
329 | |
330 | void ReportStringFunctionMemoryRangesOverlap(const char *function, |
331 | const char *offset1, uptr length1, |
332 | const char *offset2, uptr length2, |
333 | BufferedStackTrace *stack) { |
334 | ScopedInErrorReport in_report; |
335 | ErrorStringFunctionMemoryRangesOverlap error( |
336 | GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2, |
337 | length2, function); |
338 | in_report.ReportError(description: error); |
339 | } |
340 | |
341 | void ReportStringFunctionSizeOverflow(uptr offset, uptr size, |
342 | BufferedStackTrace *stack) { |
343 | ScopedInErrorReport in_report; |
344 | ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset, |
345 | size); |
346 | in_report.ReportError(description: error); |
347 | } |
348 | |
349 | void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, |
350 | uptr old_mid, uptr new_mid, |
351 | BufferedStackTrace *stack) { |
352 | ScopedInErrorReport in_report; |
353 | ErrorBadParamsToAnnotateContiguousContainer error( |
354 | GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid); |
355 | in_report.ReportError(description: error); |
356 | } |
357 | |
358 | void ReportBadParamsToAnnotateDoubleEndedContiguousContainer( |
359 | uptr storage_beg, uptr storage_end, uptr old_container_beg, |
360 | uptr old_container_end, uptr new_container_beg, uptr new_container_end, |
361 | BufferedStackTrace *stack) { |
362 | ScopedInErrorReport in_report; |
363 | ErrorBadParamsToAnnotateDoubleEndedContiguousContainer error( |
364 | GetCurrentTidOrInvalid(), stack, storage_beg, storage_end, |
365 | old_container_beg, old_container_end, new_container_beg, |
366 | new_container_end); |
367 | in_report.ReportError(description: error); |
368 | } |
369 | |
370 | void ReportODRViolation(const __asan_global *g1, u32 stack_id1, |
371 | const __asan_global *g2, u32 stack_id2) { |
372 | ScopedInErrorReport in_report; |
373 | ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2, |
374 | stack_id2); |
375 | in_report.ReportError(description: error); |
376 | } |
377 | |
378 | // ----------------------- CheckForInvalidPointerPair ----------- {{{1 |
379 | static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, |
380 | uptr a1, uptr a2) { |
381 | ScopedInErrorReport in_report; |
382 | ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2); |
383 | in_report.ReportError(description: error); |
384 | } |
385 | |
386 | static bool IsInvalidPointerPair(uptr a1, uptr a2) { |
387 | if (a1 == a2) |
388 | return false; |
389 | |
390 | // 256B in shadow memory can be iterated quite fast |
391 | static const uptr kMaxOffset = 2048; |
392 | |
393 | uptr left = a1 < a2 ? a1 : a2; |
394 | uptr right = a1 < a2 ? a2 : a1; |
395 | uptr offset = right - left; |
396 | if (offset <= kMaxOffset) |
397 | return __asan_region_is_poisoned(beg: left, size: offset); |
398 | |
399 | AsanThread *t = GetCurrentThread(); |
400 | |
401 | // check whether left is a stack memory pointer |
402 | if (uptr shadow_offset1 = t->GetStackVariableShadowStart(addr: left)) { |
403 | uptr shadow_offset2 = t->GetStackVariableShadowStart(addr: right); |
404 | return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2; |
405 | } |
406 | |
407 | // check whether left is a heap memory address |
408 | HeapAddressDescription hdesc1, hdesc2; |
409 | if (GetHeapAddressInformation(addr: left, access_size: 0, descr: &hdesc1) && |
410 | hdesc1.chunk_access.access_type == kAccessTypeInside) |
411 | return !GetHeapAddressInformation(addr: right, access_size: 0, descr: &hdesc2) || |
412 | hdesc2.chunk_access.access_type != kAccessTypeInside || |
413 | hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin; |
414 | |
415 | // check whether left is an address of a global variable |
416 | GlobalAddressDescription gdesc1, gdesc2; |
417 | if (GetGlobalAddressInformation(addr: left, access_size: 0, descr: &gdesc1)) |
418 | return !GetGlobalAddressInformation(addr: right - 1, access_size: 0, descr: &gdesc2) || |
419 | !gdesc1.PointsInsideTheSameVariable(other: gdesc2); |
420 | |
421 | if (t->GetStackVariableShadowStart(addr: right) || |
422 | GetHeapAddressInformation(addr: right, access_size: 0, descr: &hdesc2) || |
423 | GetGlobalAddressInformation(addr: right - 1, access_size: 0, descr: &gdesc2)) |
424 | return true; |
425 | |
426 | // At this point we know nothing about both a1 and a2 addresses. |
427 | return false; |
428 | } |
429 | |
430 | static inline void CheckForInvalidPointerPair(void *p1, void *p2) { |
431 | switch (flags()->detect_invalid_pointer_pairs) { |
432 | case 0: |
433 | return; |
434 | case 1: |
435 | if (p1 == nullptr || p2 == nullptr) |
436 | return; |
437 | break; |
438 | } |
439 | |
440 | uptr a1 = reinterpret_cast<uptr>(p1); |
441 | uptr a2 = reinterpret_cast<uptr>(p2); |
442 | |
443 | if (IsInvalidPointerPair(a1, a2)) { |
444 | GET_CALLER_PC_BP_SP; |
445 | ReportInvalidPointerPair(pc, bp, sp, a1, a2); |
446 | } |
447 | } |
448 | // ----------------------- Mac-specific reports ----------------- {{{1 |
449 | |
450 | void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, |
451 | BufferedStackTrace *stack) { |
452 | ScopedInErrorReport in_report; |
453 | Printf( |
454 | format: "mz_realloc(%p) -- attempting to realloc unallocated memory.\n" |
455 | "This is an unrecoverable problem, exiting now.\n" , |
456 | (void *)addr); |
457 | PrintZoneForPointer(ptr: addr, zone_ptr, zone_name); |
458 | stack->Print(); |
459 | DescribeAddressIfHeap(addr); |
460 | } |
461 | |
462 | // -------------- SuppressErrorReport -------------- {{{1 |
463 | // Avoid error reports duplicating for ASan recover mode. |
464 | static bool SuppressErrorReport(uptr pc) { |
465 | if (!common_flags()->suppress_equal_pcs) return false; |
466 | for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { |
467 | uptr cmp = atomic_load_relaxed(a: &AsanBuggyPcPool[i]); |
468 | if (cmp == 0 && atomic_compare_exchange_strong(a: &AsanBuggyPcPool[i], cmp: &cmp, |
469 | xchg: pc, mo: memory_order_relaxed)) |
470 | return false; |
471 | if (cmp == pc) return true; |
472 | } |
473 | Die(); |
474 | } |
475 | |
476 | void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, |
477 | uptr access_size, u32 exp, bool fatal) { |
478 | if (__asan_test_only_reported_buggy_pointer) { |
479 | *__asan_test_only_reported_buggy_pointer = addr; |
480 | return; |
481 | } |
482 | if (!fatal && SuppressErrorReport(pc)) return; |
483 | ENABLE_FRAME_POINTER; |
484 | |
485 | // Optimization experiments. |
486 | // The experiments can be used to evaluate potential optimizations that remove |
487 | // instrumentation (assess false negatives). Instead of completely removing |
488 | // some instrumentation, compiler can emit special calls into runtime |
489 | // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass |
490 | // mask of experiments (exp). |
491 | // The reaction to a non-zero value of exp is to be defined. |
492 | (void)exp; |
493 | |
494 | ScopedInErrorReport in_report(fatal); |
495 | ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write, |
496 | access_size); |
497 | in_report.ReportError(description: error); |
498 | } |
499 | |
500 | } // namespace __asan |
501 | |
502 | // --------------------------- Interface --------------------- {{{1 |
503 | using namespace __asan; |
504 | |
505 | void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, |
506 | uptr access_size, u32 exp) { |
507 | ENABLE_FRAME_POINTER; |
508 | bool fatal = flags()->halt_on_error; |
509 | ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); |
510 | } |
511 | |
512 | void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { |
513 | Lock l(&error_message_buf_mutex); |
514 | error_report_callback = callback; |
515 | } |
516 | |
517 | void __asan_describe_address(uptr addr) { |
518 | // Thread registry must be locked while we're describing an address. |
519 | asanThreadRegistry().Lock(); |
520 | PrintAddressDescription(addr, access_size: 1, bug_type: "" ); |
521 | asanThreadRegistry().Unlock(); |
522 | } |
523 | |
524 | int __asan_report_present() { |
525 | return ScopedInErrorReport::CurrentError().kind != kErrorKindInvalid; |
526 | } |
527 | |
528 | uptr __asan_get_report_pc() { |
529 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
530 | return ScopedInErrorReport::CurrentError().Generic.pc; |
531 | return 0; |
532 | } |
533 | |
534 | uptr __asan_get_report_bp() { |
535 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
536 | return ScopedInErrorReport::CurrentError().Generic.bp; |
537 | return 0; |
538 | } |
539 | |
540 | uptr __asan_get_report_sp() { |
541 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
542 | return ScopedInErrorReport::CurrentError().Generic.sp; |
543 | return 0; |
544 | } |
545 | |
546 | uptr __asan_get_report_address() { |
547 | ErrorDescription &err = ScopedInErrorReport::CurrentError(); |
548 | if (err.kind == kErrorKindGeneric) |
549 | return err.Generic.addr_description.Address(); |
550 | else if (err.kind == kErrorKindDoubleFree) |
551 | return err.DoubleFree.addr_description.addr; |
552 | return 0; |
553 | } |
554 | |
555 | int __asan_get_report_access_type() { |
556 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
557 | return ScopedInErrorReport::CurrentError().Generic.is_write; |
558 | return 0; |
559 | } |
560 | |
561 | uptr __asan_get_report_access_size() { |
562 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
563 | return ScopedInErrorReport::CurrentError().Generic.access_size; |
564 | return 0; |
565 | } |
566 | |
567 | const char *__asan_get_report_description() { |
568 | if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) |
569 | return ScopedInErrorReport::CurrentError().Generic.bug_descr; |
570 | return ScopedInErrorReport::CurrentError().Base.scariness.GetDescription(); |
571 | } |
572 | |
573 | extern "C" { |
574 | SANITIZER_INTERFACE_ATTRIBUTE |
575 | void __sanitizer_ptr_sub(void *a, void *b) { |
576 | CheckForInvalidPointerPair(p1: a, p2: b); |
577 | } |
578 | SANITIZER_INTERFACE_ATTRIBUTE |
579 | void __sanitizer_ptr_cmp(void *a, void *b) { |
580 | CheckForInvalidPointerPair(p1: a, p2: b); |
581 | } |
582 | } // extern "C" |
583 | |
584 | // Provide default implementation of __asan_on_error that does nothing |
585 | // and may be overriden by user. |
586 | SANITIZER_INTERFACE_WEAK_DEF(void, __asan_on_error, void) {} |
587 | |