Index: compiler-rt/trunk/lib/asan/asan_report.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_report.cc +++ compiler-rt/trunk/lib/asan/asan_report.cc @@ -31,6 +31,8 @@ static char *error_message_buffer = nullptr; static uptr error_message_buffer_pos = 0; static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); +static const unsigned kAsanBuggyPcPoolSize = 25; +static __sanitizer::atomic_uint64_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; struct ReportData { uptr pc; @@ -1000,8 +1002,23 @@ DescribeHeapAddress(addr, 1); } +// -------------- SuppressErrorReport -------------- {{{1 +// Avoid error reports duplicating for ASan recover mode. +static bool SuppressErrorReport(uptr pc) { + if (!common_flags()->suppress_equal_pcs) return false; + for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { + u64 cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]); + if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp, + pc, memory_order_relaxed)) + return false; + if (cmp == pc) return true; + } + Die(); +} + void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, uptr access_size, u32 exp, bool fatal) { + if (!fatal && SuppressErrorReport(pc)) return; ENABLE_FRAME_POINTER; // Optimization experiments. Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.inc @@ -192,3 +192,6 @@ bool, abort_on_error, SANITIZER_MAC, "If set, the tool calls abort() instead of _exit() after printing the " "error report.") +COMMON_FLAG(bool, suppress_equal_pcs, true, + "Deduplicate multiple reports for single source location in " + "halt_on_error=false mode (asan only).") Index: compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-signals.c =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-signals.c +++ compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-signals.c @@ -3,7 +3,7 @@ // RUN: %clang_asan -fsanitize-recover=address -pthread %s -o %t // // RUN: rm -f %t.log -// RUN: %env_asan_opts=halt_on_error=false %run %t 100 >%t.log 2>&1 || true +// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t 100 >%t.log 2>&1 || true // Collision will almost always get triggered but we still need to check the unlikely case: // RUN: FileCheck --check-prefix=CHECK-COLLISION %s < %t.log || FileCheck --check-prefix=CHECK-NO-COLLISION %s < %t.log Index: compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-torture.cc =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-torture.cc +++ compiler-rt/trunk/test/asan/TestCases/Posix/halt_on_error-torture.cc @@ -2,12 +2,18 @@ // // 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:suppress_equal_pcs=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:suppress_equal_pcs=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 +// +// 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 // This one is racy although _very_ unlikely to fail: // RUN: FileCheck %s < 10.txt Index: compiler-rt/trunk/test/asan/TestCases/halt_on_error_suppress_equal_pcs.cc =================================================================== --- compiler-rt/trunk/test/asan/TestCases/halt_on_error_suppress_equal_pcs.cc +++ compiler-rt/trunk/test/asan/TestCases/halt_on_error_suppress_equal_pcs.cc @@ -0,0 +1,55 @@ +// Test reports dedupication for recovery mode. +// +// RUN: %clang_asan -fsanitize-recover=address %s -o %t +// +// Check for reports dedupication. +// RUN: %env_asan_opts=halt_on_error=false %run %t 2>&1 | FileCheck %s +// +// Check that we die after reaching different reports number threshold. +// RUN: %env_asan_opts=halt_on_error=false not %run %t 1 > %t1.log 2>&1 +// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t1.log) -eq 25 ] +// +// Check suppress_equal_pcs=true behavior is equal to default one. +// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=true %run %t 2>&1 | FileCheck %s +// +// Check suppress_equal_pcs=false behavior isn't equal to default one. +// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t > %t2.log 2>&1 +// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t2.log) -eq 30 ] + +#define ACCESS_ARRAY_FIVE_ELEMENTS(array, i) \ + array[i] = i; \ + array[i + 1] = i + 1; \ + array[i + 2] = i + 2; \ + array[i + 3] = i + 3; \ + array[i + 4] = i + 4; \ + +volatile int ten = 10; +unsigned kNumIterations = 10; + +int main(int argc, char **argv) { + char a[10]; + char b[10]; + + if (argc == 1) { + for (int i = 0; i < kNumIterations; ++i) { + // CHECK: READ of size 1 + volatile int res = a[ten + i]; + // CHECK: WRITE of size 1 + a[i + ten] = res + 3; + // CHECK: READ of size 1 + res = a[ten + i]; + // CHECK-NOT: ERROR + } + } else { + for (int i = 0; i < kNumIterations; ++i) { + ACCESS_ARRAY_FIVE_ELEMENTS(a, ten); + ACCESS_ARRAY_FIVE_ELEMENTS(a, ten + 5); + ACCESS_ARRAY_FIVE_ELEMENTS(a, ten + 10); + ACCESS_ARRAY_FIVE_ELEMENTS(b, ten); + ACCESS_ARRAY_FIVE_ELEMENTS(b, ten + 5); + ACCESS_ARRAY_FIVE_ELEMENTS(b, ten + 10); + } + } + return 0; +} +