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, deduplicate_reports, true, + "Deduplicate multiple reports for single source location in " + "halt_on_error = false mode.") Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -37,7 +37,10 @@ namespace __asan { +const unsigned kAsanBuggyPcPoolSize = 25; + uptr AsanMappingProfile[kAsanMappingProfileSize]; +uptr AsanBuggyPcPool[kAsanBuggyPcPoolSize]; static void AsanDie() { static atomic_uint32_t num_calls; @@ -107,6 +110,28 @@ PoisonShadow(ptr, size, kAsanInternalHeapMagic); } +// -------------- SuppressErrorReport -------------- {{{1 +// We need to avoid error reports duplicating for ASan recover mode. +static bool SuppressErrorReport(uptr pc) { + if (!flags()->deduplicate_reports) return false; + static u32 pc_num; + // We have exceeded reasonable number of different reports, subsequent + // errors are likely artificial, so exit here. + if (pc_num >= kAsanBuggyPcPoolSize) { + if (!flags()->sleep_before_dying) + SleepForMillis(100); + internal__exit(1); + } + + for (int i = pc_num; i >= 0; --i) + if (pc == AsanBuggyPcPool[i]) return true; + + AsanBuggyPcPool[pc_num] = pc; + atomic_fetch_add((__sanitizer::atomic_uint32_t *)&pc_num, 1, + memory_order_acq_rel); + return false; +} + // -------------------------- Run-time entry ------------------- {{{1 // exported functions #define ASAN_REPORT_ERROR(type, is_write, size) \ @@ -123,6 +148,7 @@ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_ ## type ## size ## _noabort(uptr addr) { \ GET_CALLER_PC_BP_SP; \ + if (SuppressErrorReport(pc)) return; \ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ } \ @@ -151,6 +177,7 @@ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \ GET_CALLER_PC_BP_SP; \ + if (SuppressErrorReport(pc)) return; \ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ } \ @@ -169,6 +196,7 @@ *__asan_test_only_reported_buggy_pointer = addr; \ } else { \ GET_CALLER_PC_BP_SP; \ + if (SuppressErrorReport(pc)) return; \ ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \ fatal); \ } \ @@ -223,6 +251,7 @@ void __asan_loadN_noabort(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; + if (SuppressErrorReport(pc)) return; ReportGenericError(pc, bp, sp, addr, false, size, 0, false); } } @@ -250,6 +279,7 @@ void __asan_storeN_noabort(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; + if (SuppressErrorReport(pc)) return; ReportGenericError(pc, bp, sp, addr, true, size, 0, false); } } 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:deduplicate_reports=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:deduplicate_reports=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 Index: test/asan/TestCases/halt_on_error-2.c =================================================================== --- /dev/null +++ test/asan/TestCases/halt_on_error-2.c @@ -0,0 +1,23 @@ +// Test reports dedupication for recovery mode. +// +// RUN: %clang_asan -fsanitize-recover=address %s -o %t +// RUN: %env_asan_opts=halt_on_error=false %run %t >2.txt 2>&1 +// RUN: FileCheck %s < 2.txt +// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' 2.txt) -eq 3 ] + +volatile int ten = 10; +unsigned kNumIterations = 10; + +int main() { + char x[10]; + for (int i = 0; i < kNumIterations; ++i) { + // CHECK: READ of size 1 + volatile int res = x[ten + i]; + // CHECK: WRITE of size 1 + x[i + ten] = res + 3; + // CHECK: READ of size 1 + res = x[ten + i]; + } + return 0; +} +