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