Index: lib/asan/asan_flags.inc =================================================================== --- lib/asan/asan_flags.inc +++ lib/asan/asan_flags.inc @@ -138,3 +138,6 @@ ASAN_FLAG(bool, halt_on_error, true, "Crash the program after printing the first error report " "(WARNING: USE AT YOUR OWN RISK!)") +ASAN_FLAG(bool, use_suppressions, true, + "Suppress multiple reports for single source location in " + "halt_on_error = false mode.") Index: lib/asan/asan_globals.cc =================================================================== --- lib/asan/asan_globals.cc +++ lib/asan/asan_globals.cc @@ -87,8 +87,8 @@ g.module_name, g.has_dynamic_init); if (g.location) { Report(" location (%p): name=%s[%p], %d %d\n", g.location, - g.location->filename, g.location->filename, g.location->line_no, - g.location->column_no); + g.location->getFilename(), g.location->getFilename(), + g.location->getLine(), g.location->getColumn()); } } Index: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ lib/asan/asan_interface_internal.h @@ -19,10 +19,12 @@ #define ASAN_INTERFACE_INTERNAL_H #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_source_location.h" #include "asan_init_version.h" using __sanitizer::uptr; +using __sanitizer::SourceLocation; extern "C" { // This function should be called at the very beginning of the process, @@ -37,11 +39,7 @@ // This structure is used to describe the source location of a place where // global was defined. - struct __asan_global_source_location { - const char *filename; - int line_no; - int column_no; - }; + typedef SourceLocation __asan_global_source_location; // This structure describes an instrumented global variable. struct __asan_global { @@ -167,18 +165,30 @@ SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size); - SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size, + SourceLocation *Sloc); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size, + SourceLocation *Sloc); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp); Index: lib/asan/asan_report.cc =================================================================== --- lib/asan/asan_report.cc +++ lib/asan/asan_report.cc @@ -270,7 +270,7 @@ const char *res = g.module_name; // Prefer the filename from source location, if is available. if (g.location) - res = g.location->filename; + res = g.location->getFilename(); CHECK(res); return res; } @@ -280,10 +280,10 @@ str->append("%s", GlobalFilename(g)); if (!g.location) return; - if (g.location->line_no) - str->append(":%d", g.location->line_no); - if (g.location->column_no) - str->append(":%d", g.location->column_no); + if (g.location->getLine()) + str->append(":%d", g.location->getLine()); + if (g.location->getColumn()) + str->append(":%d", g.location->getColumn()); } static void DescribeAddressRelativeToGlobal(uptr addr, uptr size, Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -27,6 +27,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_source_location.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "lsan/lsan_common.h" #include "ubsan/ubsan_init.h" @@ -107,6 +108,11 @@ PoisonShadow(ptr, size, kAsanInternalHeapMagic); } +static bool SuppressReport(SourceLocation *Sloc) { + SourceLocation Loc = Sloc->acquire(); + return flags()->use_suppressions && Loc.isDisabled(); +} + // -------------------------- Run-time entry ------------------- {{{1 // exported functions #define ASAN_REPORT_ERROR(type, is_write, size) \ @@ -120,11 +126,13 @@ GET_CALLER_PC_BP_SP; \ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ } \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## size ## _noabort(uptr addr) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ -} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size ## _noabort(uptr addr, \ + SourceLocation *Sloc) { \ + if (SuppressReport(Sloc)) return; \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ ASAN_REPORT_ERROR(load, false, 1) ASAN_REPORT_ERROR(load, false, 2) @@ -149,7 +157,9 @@ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \ +void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size, \ + SourceLocation *Sloc) { \ + if (SuppressReport(Sloc)) return; \ GET_CALLER_PC_BP_SP; \ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ } \ @@ -185,7 +195,8 @@ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ - void __asan_##type##size ## _noabort(uptr addr) { \ + void __asan_##type##size ## _noabort(uptr addr, SourceLocation *Sloc) { \ + if (SuppressReport(Sloc)) return; \ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \ } \ @@ -220,8 +231,8 @@ extern "C" NOINLINE INTERFACE_ATTRIBUTE -void __asan_loadN_noabort(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { +void __asan_loadN_noabort(uptr addr, uptr size, SourceLocation *Sloc) { + if (!SuppressReport(Sloc) && __asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; ReportGenericError(pc, bp, sp, addr, false, size, 0, false); } @@ -247,8 +258,8 @@ extern "C" NOINLINE INTERFACE_ATTRIBUTE -void __asan_storeN_noabort(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { +void __asan_storeN_noabort(uptr addr, uptr size, SourceLocation *Sloc) { + if (!SuppressReport(Sloc) && __asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; ReportGenericError(pc, bp, sp, addr, true, size, 0, false); } Index: lib/sanitizer_common/sanitizer_source_location.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_source_location.h @@ -0,0 +1,61 @@ +//===-- sanitizer_source_location.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 shared between AddressSanitizer and UndefinedBehaviorSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_SOURCE_LOCATION_H +#define SANITIZER_SOURCE_LOCATION_H + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __sanitizer { + +/// \brief A description of a source location. This corresponds to Clang's +/// \c PresumedLoc type. +class SourceLocation { + const char *Filename; + u32 Line; + u32 Column; + + public: + SourceLocation() : Filename(), Line(), Column() {} + SourceLocation(const char *Filename, unsigned Line, unsigned Column) + : Filename(Filename), Line(Line), Column(Column) {} + + /// \brief Determine whether the source location is known. + bool isInvalid() const { return !Filename; } + + /// \brief Atomically acquire a copy, disabling original in-place. + /// Exactly one call to acquire() returns a copy that isn't disabled. + SourceLocation acquire() { + u32 OldColumn = atomic_exchange((atomic_uint32_t *)&Column, ~u32(0), + memory_order_relaxed); + return SourceLocation(Filename, Line, OldColumn); + } + + /// \brief Determine if this Location has been disabled. + /// Disabled SourceLocations are invalid to use. + bool isDisabled() { + return Column == ~u32(0); + } + + /// \brief Get the presumed filename for the source location. + const char *getFilename() const { return Filename; } + /// \brief Get the presumed line number. + unsigned getLine() const { return Line; } + /// \brief Get the column within the presumed line. + unsigned getColumn() const { return Column; } +}; + +} // namespace __sanitizer + +#endif Index: lib/ubsan/ubsan_value.h =================================================================== --- lib/ubsan/ubsan_value.h +++ lib/ubsan/ubsan_value.h @@ -14,8 +14,7 @@ #ifndef UBSAN_VALUE_H #define UBSAN_VALUE_H -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_source_location.h" // FIXME: Move this out to a config header. #if __SIZEOF_INT128__ @@ -40,45 +39,6 @@ /// \brief Largest floating-point type we support. typedef long double FloatMax; -/// \brief A description of a source location. This corresponds to Clang's -/// \c PresumedLoc type. -class SourceLocation { - const char *Filename; - u32 Line; - u32 Column; - -public: - SourceLocation() : Filename(), Line(), Column() {} - SourceLocation(const char *Filename, unsigned Line, unsigned Column) - : Filename(Filename), Line(Line), Column(Column) {} - - /// \brief Determine whether the source location is known. - bool isInvalid() const { return !Filename; } - - /// \brief Atomically acquire a copy, disabling original in-place. - /// Exactly one call to acquire() returns a copy that isn't disabled. - SourceLocation acquire() { - u32 OldColumn = __sanitizer::atomic_exchange( - (__sanitizer::atomic_uint32_t *)&Column, ~u32(0), - __sanitizer::memory_order_relaxed); - return SourceLocation(Filename, Line, OldColumn); - } - - /// \brief Determine if this Location has been disabled. - /// Disabled SourceLocations are invalid to use. - bool isDisabled() { - return Column == ~u32(0); - } - - /// \brief Get the presumed filename for the source location. - const char *getFilename() const { return Filename; } - /// \brief Get the presumed line number. - unsigned getLine() const { return Line; } - /// \brief Get the column within the presumed line. - unsigned getColumn() const { return Column; } -}; - - /// \brief A description of a type. class TypeDescriptor { /// A value from the \c Kind enumeration, specifying what flavor of type we Index: test/asan/TestCases/Posix/halt_on_error-torture-2.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Posix/halt_on_error-torture-2.cc @@ -0,0 +1,73 @@ +// Suppressions test recovery mode with many threads. +// +// RUN: %clangxx_asan -fsanitize-recover=address -pthread %s -o %t +// +// RUN: %env_asan_opts=halt_on_error=false %run %t 1 10 >2.txt 2>&1 +// RUN: FileCheck %s < 2.txt +// RUN: [ $(grep -c 'ERROR: AddressSanitizer: use-after-poison' 2.txt) -eq 1 ] + +#include +#include +#include +#include + +#include + +size_t nthreads = 10; +size_t niter = 10; + +void random_delay(unsigned *seed) { + *seed = 1664525 * *seed + 1013904223; + struct timespec delay = { 0, (*seed % 1000) * 1000 }; + nanosleep(&delay, 0); +} + +void *run(void *arg) { + unsigned seed = (unsigned)(size_t)arg; + + volatile char tmp[2]; + __asan_poison_memory_region(&tmp, sizeof(tmp)); + + for (size_t i = 0; i < niter; ++i) { + random_delay(&seed); + // Expect error collisions here + // CHECK: ERROR: AddressSanitizer: use-after-poison + volatile int idx = 0; + tmp[idx] = 0; + } + + return 0; +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Syntax: %s nthreads niter\n", argv[0]); + exit(1); + } + + nthreads = (size_t)strtoul(argv[1], 0, 0); + niter = (size_t)strtoul(argv[2], 0, 0); + + pthread_t *tids = new pthread_t[nthreads]; + + for (size_t i = 0; i < nthreads; ++i) { + if (0 != pthread_create(&tids[i], 0, run, (void *)i)) { + fprintf(stderr, "Failed to create thread\n"); + exit(1); + } + } + + for (size_t i = 0; i < nthreads; ++i) { + if (0 != pthread_join(tids[i], 0)) { + fprintf(stderr, "Failed to join thread\n"); + exit(1); + } + } + + // CHECK-NO-COLLISION: All threads terminated + printf("All threads terminated\n"); + + delete [] tids; + + return 0; +} Index: test/asan/TestCases/Posix/halt_on_error-torture.cc =================================================================== --- test/asan/TestCases/Posix/halt_on_error-torture.cc +++ test/asan/TestCases/Posix/halt_on_error-torture.cc @@ -2,13 +2,13 @@ // // RUN: %clangxx_asan -fsanitize-recover=address -pthread %s -o %t // -// RUN: %env_asan_opts=halt_on_error=false %run %t 1 10 >1.txt 2>&1 +// RUN: %env_asan_opts=halt_on_error=false:use_suppressions=false %run %t 1 10 >1.txt 2>&1 // RUN: FileCheck %s < 1.txt // RUN: [ $(grep -c 'ERROR: AddressSanitizer: use-after-poison' 1.txt) -eq 10 ] // RUN: FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt // // Collisions are unlikely but still possible so we need the ||. -// RUN: %env_asan_opts=halt_on_error=false %run %t 10 20 >10.txt 2>&1 || true +// RUN: %env_asan_opts=halt_on_error=false:use_suppressions=false %run %t 10 20 >10.txt 2>&1 || true // This one is racy although _very_ unlikely to fail: // RUN: FileCheck %s < 10.txt // RUN: FileCheck --check-prefix=CHECK-COLLISION %s < 1.txt || FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt