Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -18,6 +18,7 @@ asan_rtl.cc asan_stack.cc asan_stats.cc + asan_suppressions.cc asan_thread.cc asan_win.cc) Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -20,6 +20,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" +#include "asan_suppressions.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { @@ -34,12 +35,16 @@ return false; } +struct AsanInterceptorContext { + const char *interceptor_name; +}; + // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, // and ASAN_WRITE_RANGE as macro instead of function so // that no extra frames are created, and stack trace contains // relevant information only. // We check all shadow bytes. -#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ +#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \ uptr __offset = (uptr)(offset); \ uptr __size = (uptr)(size); \ uptr __bad = 0; \ @@ -50,12 +55,24 @@ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ (__bad = __asan_region_is_poisoned(__offset, __size))) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ + AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ + bool suppressed = false; \ + if (_ctx) { \ + suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ + if (!suppressed && HaveStackTraceBasedSuppressions()) { \ + GET_STACK_TRACE_FATAL_HERE; \ + suppressed = IsStackTraceSuppressed(&stack); \ + } \ + } \ + if (!suppressed) \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ } \ } while (0) -#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) -#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true) +#define ASAN_READ_RANGE(ctx, offset, size) \ + ACCESS_MEMORY_RANGE(ctx, offset, size, false) +#define ASAN_WRITE_RANGE(ctx, offset, size) \ + ACCESS_MEMORY_RANGE(ctx, offset, size, true) // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. @@ -113,16 +130,21 @@ #define ASAN_INTERCEPT_FUNC(name) #endif // SANITIZER_MAC +#define ASAN_INTERCEPTOR_ENTER(ctx, func) \ + AsanInterceptorContext _ctx = {#func}; \ + ctx = (void *)&_ctx; \ + (void) ctx; \ + #define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - ASAN_WRITE_RANGE(ptr, size) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size) + ASAN_WRITE_RANGE(ctx, ptr, size) +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + ASAN_READ_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ do { \ if (asan_init_is_running) \ return REAL(func)(__VA_ARGS__); \ - ctx = 0; \ - (void) ctx; \ + ASAN_INTERCEPTOR_ENTER(ctx, func); \ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ return REAL(func)(__VA_ARGS__); \ ENSURE_ASAN_INITED(); \ @@ -151,8 +173,10 @@ #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) #include "sanitizer_common/sanitizer_common_interceptors.inc" -#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) +// Syscall interceptors don't have contexts, we don't support suppressions +// for them. +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ (void)(p); \ @@ -325,14 +349,16 @@ } INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, memcmp); if (UNLIKELY(!asan_inited)) return internal_memcmp(a1, a2, size); ENSURE_ASAN_INITED(); if (flags()->replace_intrin) { if (flags()->strict_memcmp) { // Check the entire regions even if the first bytes of the buffers are // different. - ASAN_READ_RANGE(a1, size); - ASAN_READ_RANGE(a2, size); + ASAN_READ_RANGE(ctx, a1, size); + ASAN_READ_RANGE(ctx, a2, size); // Fallthrough to REAL(memcmp) below. } else { unsigned char c1 = 0, c2 = 0; @@ -344,65 +370,81 @@ c2 = s2[i]; if (c1 != c2) break; } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); + ASAN_READ_RANGE(ctx, s1, Min(i + 1, size)); + ASAN_READ_RANGE(ctx, s2, Min(i + 1, size)); return CharCmp(c1, c2); } } return REAL(memcmp(a1, a2, size)); } +// memcpy is called during __asan_init() from the internals of printf(...). +// We do not treat memcpy with to==from as a bug. +// See http://llvm.org/bugs/show_bug.cgi?id=11763. +#define ASAN_MEMCPY_IMPL(ctx, to, from, size) do { \ + if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \ + if (asan_init_is_running) { \ + return REAL(memcpy)(to, from, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + if (to != from) { \ + CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \ + } \ + ASAN_READ_RANGE(ctx, from, size); \ + ASAN_WRITE_RANGE(ctx, to, size); \ + } \ + return REAL(memcpy)(to, from, size); \ + } while (0) + + void *__asan_memcpy(void *to, const void *from, uptr size) { - if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); - // memcpy is called during __asan_init() from the internals - // of printf(...). - if (asan_init_is_running) { - return REAL(memcpy)(to, from, size); - } - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - if (to != from) { - // We do not treat memcpy with to==from as a bug. - // See http://llvm.org/bugs/show_bug.cgi?id=11763. - CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); - } - ASAN_READ_RANGE(from, size); - ASAN_WRITE_RANGE(to, size); - } - return REAL(memcpy)(to, from, size); + ASAN_MEMCPY_IMPL(nullptr, to, from, size); } +// memset is called inside Printf. +#define ASAN_MEMSET_IMPL(ctx, block, c, size) do { \ + if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \ + if (asan_init_is_running) { \ + return REAL(memset)(block, c, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_WRITE_RANGE(ctx, block, size); \ + } \ + return REAL(memset)(block, c, size); \ + } while (0) + void *__asan_memset(void *block, int c, uptr size) { - if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); - // memset is called inside Printf. - if (asan_init_is_running) { - return REAL(memset)(block, c, size); - } - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - ASAN_WRITE_RANGE(block, size); - } - return REAL(memset)(block, c, size); + ASAN_MEMSET_IMPL(nullptr, block, c, size); } +#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) do { \ + if (UNLIKELY(!asan_inited)) \ + return internal_memmove(to, from, size); \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_READ_RANGE(ctx, from, size); \ + ASAN_WRITE_RANGE(ctx, to, size); \ + } \ + return internal_memmove(to, from, size); \ + } while (0) + void *__asan_memmove(void *to, const void *from, uptr size) { - if (UNLIKELY(!asan_inited)) - return internal_memmove(to, from, size); - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - ASAN_READ_RANGE(from, size); - ASAN_WRITE_RANGE(to, size); - } - return internal_memmove(to, from, size); + ASAN_MEMMOVE_IMPL(nullptr, to, from, size); } INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { - return __asan_memmove(to, from, size); + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, memmove); + ASAN_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, memcpy); #if !SANITIZER_MAC - return __asan_memcpy(to, from, size); + ASAN_MEMCPY_IMPL(ctx, to, from, size); #else // At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced // with WRAP(memcpy). As a result, false positives are reported for memmove() @@ -410,15 +452,19 @@ // ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with // internal_memcpy(), which may lead to crashes, see // http://llvm.org/bugs/show_bug.cgi?id=16362. - return __asan_memmove(to, from, size); + ASAN_MEMMOVE_IMPL(ctx, to, from, size); #endif // !SANITIZER_MAC } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { - return __asan_memset(block, c, size); + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, memset); + ASAN_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(char*, strchr, const char *str, int c) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strchr); if (UNLIKELY(!asan_inited)) return internal_strchr(str, c); // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is // used. @@ -429,7 +475,7 @@ char *result = REAL(strchr)(str, c); if (flags()->replace_str) { uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1; - ASAN_READ_RANGE(str, bytes_read); + ASAN_READ_RANGE(ctx, str, bytes_read); } return result; } @@ -451,13 +497,15 @@ // For both strcat() and strncat() we need to check the validity of |to| // argument irrespective of the |from| length. INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = REAL(strlen)(from); - ASAN_READ_RANGE(from, from_length + 1); + ASAN_READ_RANGE(ctx, from, from_length + 1); uptr to_length = REAL(strlen)(to); - ASAN_READ_RANGE(to, to_length); - ASAN_WRITE_RANGE(to + to_length, from_length + 1); + ASAN_READ_RANGE(ctx, to, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); // If the copying actually happens, the |from| string should not overlap // with the resulting string starting at |to|, which has a length of // to_length + from_length + 1. @@ -470,14 +518,16 @@ } INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strncat); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = MaybeRealStrnlen(from, size); uptr copy_length = Min(size, from_length + 1); - ASAN_READ_RANGE(from, copy_length); + ASAN_READ_RANGE(ctx, from, copy_length); uptr to_length = REAL(strlen)(to); - ASAN_READ_RANGE(to, to_length); - ASAN_WRITE_RANGE(to + to_length, from_length + 1); + ASAN_READ_RANGE(ctx, to, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); if (from_length > 0) { CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, from, copy_length); @@ -487,6 +537,8 @@ } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT #endif @@ -499,19 +551,21 @@ if (flags()->replace_str) { uptr from_size = REAL(strlen)(from) + 1; CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); - ASAN_READ_RANGE(from, from_size); - ASAN_WRITE_RANGE(to, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, from_size); } return REAL(strcpy)(to, from); // NOLINT } #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strdup); if (UNLIKELY(!asan_inited)) return internal_strdup(s); ENSURE_ASAN_INITED(); uptr length = REAL(strlen)(s); if (flags()->replace_str) { - ASAN_READ_RANGE(s, length + 1); + ASAN_READ_RANGE(ctx, s, length + 1); } GET_STACK_TRACE_MALLOC; void *new_mem = asan_malloc(length + 1, &stack); @@ -521,6 +575,8 @@ #endif INTERCEPTOR(SIZE_T, strlen, const char *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strlen); if (UNLIKELY(!asan_inited)) return internal_strlen(s); // strlen is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. @@ -530,37 +586,43 @@ ENSURE_ASAN_INITED(); SIZE_T length = REAL(strlen)(s); if (flags()->replace_str) { - ASAN_READ_RANGE(s, length + 1); + ASAN_READ_RANGE(ctx, s, length + 1); } return length; } INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, wcslen); SIZE_T length = REAL(wcslen)(s); if (!asan_init_is_running) { ENSURE_ASAN_INITED(); - ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); + ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t)); } return length; } INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strncpy); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); - ASAN_READ_RANGE(from, from_size); - ASAN_WRITE_RANGE(to, size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, size); } return REAL(strncpy)(to, from, size); } #if ASAN_INTERCEPT_STRNLEN INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strnlen); ENSURE_ASAN_INITED(); uptr length = REAL(strnlen)(s, maxlen); if (flags()->replace_str) { - ASAN_READ_RANGE(s, Min(length + 1, maxlen)); + ASAN_READ_RANGE(ctx, s, Min(length + 1, maxlen)); } return length; } @@ -585,6 +647,8 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strtol); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtol)(nptr, endptr, base); @@ -596,12 +660,14 @@ } if (IsValidStrtolBase(base)) { FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); } return result; } INTERCEPTOR(int, atoi, const char *nptr) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atoi); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr); #endif @@ -616,11 +682,13 @@ // different from int). So, we just imitate this behavior. int result = REAL(strtol)(nptr, &real_endptr, 10); FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); return result; } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atol); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr); #endif @@ -631,13 +699,15 @@ char *real_endptr; long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); return result; } #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT char **endptr, int base) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strtoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtoll)(nptr, endptr, base); @@ -652,12 +722,14 @@ // if base is valid. if (IsValidStrtolBase(base)) { FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); } return result; } INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoll)(nptr); @@ -665,7 +737,7 @@ char *real_endptr; long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); return result; } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -21,6 +21,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" +#include "asan_suppressions.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" @@ -596,6 +597,8 @@ InitializeAsanInterceptors(); + InitializeSuppressions(); + // Enable system log ("adb logcat") on Android. // Doing this before interceptors are initialized crashes in: // AsanInitInternal -> android_log_write -> __interceptor_strcmp Index: lib/asan/asan_suppressions.h =================================================================== --- lib/asan/asan_suppressions.h +++ lib/asan/asan_suppressions.h @@ -0,0 +1,29 @@ +//===-- asan_suppressions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_suppressions.cc. +//===----------------------------------------------------------------------===// +#ifndef ASAN_SUPPRESSIONS_H +#define ASAN_SUPPRESSIONS_H + +#include "asan_internal.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __asan { + +void InitializeSuppressions(); +bool IsInterceptorSuppressed(const char *interceptor_name); +bool HaveStackTraceBasedSuppressions(); +bool IsStackTraceSuppressed(StackTrace *stack); + +} // namespace __asan + +#endif // ASAN_SUPPRESSIONS_H Index: lib/asan/asan_suppressions.cc =================================================================== --- lib/asan/asan_suppressions.cc +++ lib/asan/asan_suppressions.cc @@ -0,0 +1,84 @@ +//===-- asan_suppressions.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Issue suppression and suppression-related functions. +//===----------------------------------------------------------------------===// + +#include "asan_suppressions.h" + +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +static bool suppressions_inited = false; + +void InitializeSuppressions() { + CHECK(!suppressions_inited); + SuppressionContext::InitIfNecessary(); + suppressions_inited = true; +} + +bool IsInterceptorSuppressed(const char *interceptor_name) { + CHECK(suppressions_inited); + SuppressionContext *ctx = SuppressionContext::Get(); + Suppression *s; + // Match "interceptor_name" suppressions. + return ctx->Match(interceptor_name, SuppressionInterceptorName, &s); +} + +bool HaveStackTraceBasedSuppressions() { + CHECK(suppressions_inited); + SuppressionContext *ctx = SuppressionContext::Get(); + return ctx->HasSuppressionType(SuppressionInterceptorViaFunction) + || ctx->HasSuppressionType(SuppressionInterceptorViaLibrary); +} + +bool IsStackTraceSuppressed(StackTrace *stack) { + CHECK(suppressions_inited); + if (!HaveStackTraceBasedSuppressions()) + return false; + + SuppressionContext *ctx = SuppressionContext::Get(); + Suppression *s; + for (uptr i = 0; i < stack->size && stack->trace[i]; i++) { + uptr addr = stack->trace[i]; + Symbolizer *symbolizer = Symbolizer::GetOrInit(); + + if (ctx->HasSuppressionType(SuppressionInterceptorViaLibrary)) { + const char *module_name; + uptr module_offset; + if (symbolizer->GetModuleNameAndOffsetForPC(addr, &module_name, + &module_offset)) { + // Match "interceptor_via_library" suppressions. + if (ctx->Match(module_name, SuppressionInterceptorViaLibrary, &s)) + return true; + } + } + + if (ctx->HasSuppressionType(SuppressionInterceptorViaFunction)) { + SymbolizedStack *frames = symbolizer->SymbolizePC(addr); + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + const char *function_name = cur->info.function; + // Match "interceptor_via_function" suppressions. + if (ctx->Match(function_name, SuppressionInterceptorViaFunction, &s)) { + frames->ClearAll(); + return true; + } + } + frames->ClearAll(); + } + } + return false; +} + +} // namespace __asan Index: lib/sanitizer_common/sanitizer_suppressions.h =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.h +++ lib/sanitizer_common/sanitizer_suppressions.h @@ -28,6 +28,9 @@ SuppressionLib, SuppressionDeadlock, SuppressionVptrCheck, + SuppressionInterceptorName, + SuppressionInterceptorViaFunction, + SuppressionInterceptorViaLibrary, SuppressionTypeCount }; Index: lib/sanitizer_common/sanitizer_suppressions.cc =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.cc +++ lib/sanitizer_common/sanitizer_suppressions.cc @@ -23,7 +23,8 @@ static const char *const kTypeStrings[SuppressionTypeCount] = { "none", "race", "mutex", "thread", "signal", - "leak", "called_from_lib", "deadlock", "vptr_check"}; + "leak", "called_from_lib", "deadlock", "vptr_check", + "interceptor_name", "interceptor_via_function", "interceptor_via_library"}; bool TemplateMatch(char *templ, const char *str) { if (str == 0 || str[0] == 0) Index: lib/sanitizer_common/tests/sanitizer_suppressions_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -71,8 +71,14 @@ !internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck), "vptr_check")); + CHECK(!internal_strcmp(SuppressionTypeString( + SuppressionInterceptorName), "interceptor_name")); + CHECK(!internal_strcmp(SuppressionTypeString( + SuppressionInterceptorViaFunction), "interceptor_via_function")); + CHECK(!internal_strcmp(SuppressionTypeString( + SuppressionInterceptorViaLibrary), "interceptor_via_library")); // Ensure this test is up-to-date when suppression types are added. - CHECK_EQ(9, SuppressionTypeCount); + CHECK_EQ(12, SuppressionTypeCount); } class SuppressionContextTest : public ::testing::Test { Index: test/asan/TestCases/Darwin/suppressions-darwin.cc =================================================================== --- test/asan/TestCases/Darwin/suppressions-darwin.cc +++ test/asan/TestCases/Darwin/suppressions-darwin.cc @@ -0,0 +1,28 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t -framework Foundation +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// Check that suppressing the interceptor by name works. +// RUN: echo "interceptor_name:memmove" > %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// Check that suppressing all reports from a library works. +// RUN: echo "interceptor_via_library:CoreFoundation" > %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +// Check that suppressing library works even without the symbolizer. +// RUN: ASAN_OPTIONS=suppressions=%tmp ASAN_SYMBOLIZER_PATH=/dev/null %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include + +int main() { + char *a = (char *)malloc(6); + strcpy(a, "hello"); + CFStringRef str = CFStringCreateWithBytes(kCFAllocatorDefault, (unsigned char *)a, 10, kCFStringEncodingUTF8, FALSE); // BOOM + fprintf(stderr, "Ignored.\n"); +} + +// CHECK-CRASH: AddressSanitizer: heap-buffer-overflow +// CHECK-CRASH-NOT: Ignored. +// CHECK-IGNORE: Ignored. +// CHECK-IGNORE-NOT: AddressSanitizer: heap-buffer-overflow Index: test/asan/TestCases/suppressions-function.cc =================================================================== --- test/asan/TestCases/suppressions-function.cc +++ test/asan/TestCases/suppressions-function.cc @@ -0,0 +1,26 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_via_function:crash_function" > %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include +#include +#include + +void crash_function() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, len = %zu\n", len); +} + +int main() { + crash_function(); +} + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free Index: test/asan/TestCases/suppressions-instrumented-code.cc =================================================================== --- test/asan/TestCases/suppressions-instrumented-code.cc +++ test/asan/TestCases/suppressions-instrumented-code.cc @@ -0,0 +1,36 @@ +// Check that reports from instrumented code cannot be suppressed. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: echo "interceptor_name:memset" > %tmp +// RUN: echo "interceptor_via_function:main" >> %tmp +// RUN: echo "interceptor_via_library:%t" >> %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp not %run %t 1 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=suppressions=%tmp not %run %t 2 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=suppressions=%tmp not %run %t 3 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=suppressions=%tmp not %run %t 4 2>&1 | FileCheck %s + +#include +#include +#include + +int main(int argc, char *argv[]) { + char *a = (char *)malloc(6); + free(a); + switch (argv[1][0]) { + case '1': + a[0] = 'A'; // BOOM + break; + case '2': + memset(a, 'A', 6); // BOOM + break; + case '3': + memcpy(a, a, 6); // BOOM + break; + case '4': + memmove(a, a, 6); // BOOM + break; + } + fprintf(stderr, "ignored\n"); +} + +// CHECK: AddressSanitizer: heap-use-after-free +// CHECK-NOT: ignored Index: test/asan/TestCases/suppressions-interceptor.cc =================================================================== --- test/asan/TestCases/suppressions-interceptor.cc +++ test/asan/TestCases/suppressions-interceptor.cc @@ -0,0 +1,22 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_name:strlen" > %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include +#include +#include + +int main() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, len = %zu\n", len); +} + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free Index: test/asan/TestCases/suppressions-library.cc =================================================================== --- test/asan/TestCases/suppressions-library.cc +++ test/asan/TestCases/suppressions-library.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s %t-so.so -o %t + +// Check that without suppressions, we catch the issue. +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// RUN: echo "interceptor_via_library:%t-so.so" > %tmp +// RUN: ASAN_OPTIONS=suppressions=%tmp %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include +#include +#include + +#if !defined(SHARED_LIB) + +void crash_function(); + +int main(int argc, char *argv[]) { + crash_function(); + return 0; +} + +#else // SHARED_LIB + +void crash_function() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, %zu\n", len); +} + +#endif // SHARED_LIB + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-CRASH-NOT: strlen ignored +// CHECK-IGNORE: strlen ignored +// CHECK-IGNORE-NOT: AddressSanitizer: heap-use-after-free