Index: compiler-rt/trunk/lib/asan/asan_flags.inc =================================================================== --- compiler-rt/trunk/lib/asan/asan_flags.inc +++ compiler-rt/trunk/lib/asan/asan_flags.inc @@ -134,3 +134,6 @@ ASAN_FLAG(bool, dump_instruction_bytes, false, "If true, dump 16 bytes starting at the instruction that caused SEGV") ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") +ASAN_FLAG(bool, halt_on_error, true, + "Crash the program after printing the first error report " + "(WARNING: USE AT YOUR OWN RISK!)") Index: compiler-rt/trunk/lib/asan/asan_interceptors.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_interceptors.cc +++ compiler-rt/trunk/lib/asan/asan_interceptors.cc @@ -75,7 +75,7 @@ } \ if (!suppressed) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \ + ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\ } \ } \ } while (0) Index: compiler-rt/trunk/lib/asan/asan_interface_internal.h =================================================================== --- compiler-rt/trunk/lib/asan/asan_interface_internal.h +++ compiler-rt/trunk/lib/asan/asan_interface_internal.h @@ -167,6 +167,19 @@ 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_exp_load1(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp); Index: compiler-rt/trunk/lib/asan/asan_report.h =================================================================== --- compiler-rt/trunk/lib/asan/asan_report.h +++ compiler-rt/trunk/lib/asan/asan_report.h @@ -49,45 +49,41 @@ void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. -void NORETURN ReportStackOverflow(const SignalContext &sig); -void NORETURN ReportDeadlySignal(const char* description, - const SignalContext &sig); -void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, - BufferedStackTrace *free_stack); -void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); -void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); -void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, - AllocType alloc_type, - AllocType dealloc_type); -void NORETURN - ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); -void NORETURN - ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, - BufferedStackTrace *stack); -void NORETURN - ReportStringFunctionMemoryRangesOverlap(const char *function, - const char *offset1, uptr length1, - const char *offset2, uptr length2, - BufferedStackTrace *stack); -void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size, - BufferedStackTrace *stack); -void NORETURN - ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, - uptr old_mid, uptr new_mid, - BufferedStackTrace *stack); - -void NORETURN -ReportODRViolation(const __asan_global *g1, u32 stack_id1, - const __asan_global *g2, u32 stack_id2); +void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, + uptr access_size, u32 exp, bool fatal); +void ReportStackOverflow(const SignalContext &sig); +void ReportDeadlySignal(const char *description, const SignalContext &sig); +void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, + BufferedStackTrace *free_stack); +void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); +void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); +void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, + AllocType alloc_type, + AllocType dealloc_type); +void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); +void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, + BufferedStackTrace *stack); +void ReportStringFunctionMemoryRangesOverlap(const char *function, + const char *offset1, uptr length1, + const char *offset2, uptr length2, + BufferedStackTrace *stack); +void ReportStringFunctionSizeOverflow(uptr offset, uptr size, + BufferedStackTrace *stack); +void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, + uptr old_mid, uptr new_mid, + BufferedStackTrace *stack); + +void ReportODRViolation(const __asan_global *g1, u32 stack_id1, + const __asan_global *g2, u32 stack_id2); // Mac-specific errors and warnings. void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack); -void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, - const char *zone_name, - BufferedStackTrace *stack); -void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, - const char *zone_name, - BufferedStackTrace *stack); +void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, + const char *zone_name, + BufferedStackTrace *stack); +void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, + const char *zone_name, + BufferedStackTrace *stack); } // namespace __asan 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 @@ -622,41 +622,56 @@ // immediately after printing error report. class ScopedInErrorReport { public: - explicit ScopedInErrorReport(ReportData *report = nullptr) { - static atomic_uint32_t num_calls; - static u32 reporting_thread_tid; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + explicit ScopedInErrorReport(ReportData *report = nullptr, + bool fatal = false) { + halt_on_error_ = fatal || flags()->halt_on_error; + + if (lock_.TryLock()) { + StartReporting(report); + return; + } + + // ASan found two bugs in different threads simultaneously. + + u32 current_tid = GetCurrentTidOrInvalid(); + if (current_tid == reporting_thread_tid_ || current_tid == kInvalidTid) { + // This is either asynch signal or nested error during error reporting. + // Fail fast to avoid deadlocks. + + // Can't use Report() here because of potential deadlocks + // in nested signal handlers. + const char msg[] = "AddressSanitizer: nested bug in the same thread, " + "aborting.\n"; + WriteToFile(kStderrFd, msg, sizeof(msg)); + + internal__exit(common_flags()->exitcode); + } + + if (halt_on_error_) { // Do not print more than one report, otherwise they will mix up. // Error reporting functions shouldn't return at this situation, as - // they are defined as no-return. + // they are effectively no-returns. + Report("AddressSanitizer: while reporting a bug found another one. " - "Ignoring.\n"); - u32 current_tid = GetCurrentTidOrInvalid(); - if (current_tid != reporting_thread_tid) { - // ASan found two bugs in different threads simultaneously. Sleep - // long enough to make sure that the thread which started to print - // an error report will finish doing it. - SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); - } + "Ignoring.\n"); + + // Sleep long enough to make sure that the thread which started + // to print an error report will finish doing it. + SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); + // If we're still not dead for some reason, use raw _exit() instead of // Die() to bypass any additional checks. internal__exit(common_flags()->exitcode); + } else { + // The other thread will eventually finish reporting + // so it's safe to wait + lock_.Lock(); } - if (report) report_data = *report; - report_happened = true; - ASAN_ON_ERROR(); - // Make sure the registry and sanitizer report mutexes are locked while - // we're printing an error report. - // We can lock them only here to avoid self-deadlock in case of - // recursive reports. - asanThreadRegistry().Lock(); - CommonSanitizerReportMutex.Lock(); - reporting_thread_tid = GetCurrentTidOrInvalid(); - Printf("====================================================" - "=============\n"); + + StartReporting(report); } - // Destructor is NORETURN, as functions that report errors are. - NORETURN ~ScopedInErrorReport() { + + ~ScopedInErrorReport() { // Make sure the current thread is announced. DescribeThread(GetCurrentThread()); // We may want to grab this lock again when printing stats. @@ -667,11 +682,39 @@ if (error_report_callback) { error_report_callback(error_message_buffer); } - Report("ABORTING\n"); - Die(); + CommonSanitizerReportMutex.Unlock(); + reporting_thread_tid_ = kInvalidTid; + lock_.Unlock(); + if (halt_on_error_) { + Report("ABORTING\n"); + Die(); + } } + + private: + void StartReporting(ReportData *report) { + if (report) report_data = *report; + report_happened = true; + ASAN_ON_ERROR(); + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + CommonSanitizerReportMutex.Lock(); + reporting_thread_tid_ = GetCurrentTidOrInvalid(); + Printf("====================================================" + "=============\n"); + } + + static StaticSpinMutex lock_; + static u32 reporting_thread_tid_; + bool halt_on_error_; }; +StaticSpinMutex ScopedInErrorReport::lock_; +u32 ScopedInErrorReport::reporting_thread_tid_; + void ReportStackOverflow(const SignalContext &sig) { ScopedInErrorReport in_report; Decorator d; @@ -688,7 +731,7 @@ } void ReportDeadlySignal(const char *description, const SignalContext &sig) { - ScopedInErrorReport in_report; + ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true); Decorator d; Printf("%s", d.Warning()); Report( @@ -1034,7 +1077,7 @@ ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, bug_descr }; - ScopedInErrorReport in_report(&report); + ScopedInErrorReport in_report(&report, fatal); Decorator d; Printf("%s", d.Warning()); @@ -1060,6 +1103,18 @@ PrintShadowMemoryForAddress(addr); } +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, + uptr access_size, u32 exp) { + ENABLE_FRAME_POINTER; + bool fatal = flags()->halt_on_error; + ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); +} + void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { error_report_callback = callback; if (callback) { Index: compiler-rt/trunk/lib/asan/asan_rtl.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_rtl.cc +++ compiler-rt/trunk/lib/asan/asan_rtl.cc @@ -113,13 +113,18 @@ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_ ## type ## size(uptr addr) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ -} + 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); \ +} \ ASAN_REPORT_ERROR(load, false, 1) ASAN_REPORT_ERROR(load, false, 2) @@ -132,22 +137,27 @@ ASAN_REPORT_ERROR(store, true, 8) ASAN_REPORT_ERROR(store, true, 16) -#define ASAN_REPORT_ERROR_N(type, is_write) \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ - GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +#define ASAN_REPORT_ERROR_N(type, is_write) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ -} + 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) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ ASAN_REPORT_ERROR_N(load, false) ASAN_REPORT_ERROR_N(store, true) -#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \ +#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \ uptr sp = MEM_TO_SHADOW(addr); \ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast(sp) \ : *reinterpret_cast(sp); \ @@ -159,7 +169,8 @@ *__asan_test_only_reported_buggy_pointer = addr; \ } else { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \ + ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \ + fatal); \ } \ } \ } @@ -167,12 +178,16 @@ #define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_##type##size(uptr addr) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0) \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_exp_##type##size(uptr addr, u32 exp) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp) \ - } + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \ + } \ + extern "C" NOINLINE INTERFACE_ATTRIBUTE \ + void __asan_##type##size ## _noabort(uptr addr) { \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \ + } \ ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) @@ -190,7 +205,7 @@ void __asan_loadN(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, false, size, 0); + ReportGenericError(pc, bp, sp, addr, false, size, 0, true); } } @@ -199,7 +214,16 @@ void __asan_exp_loadN(uptr addr, uptr size, u32 exp) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, false, size, exp); + ReportGenericError(pc, bp, sp, addr, false, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_loadN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, false, size, 0, false); } } @@ -208,7 +232,7 @@ void __asan_storeN(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, true, size, 0); + ReportGenericError(pc, bp, sp, addr, true, size, 0, true); } } @@ -217,7 +241,16 @@ void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, true, size, exp); + ReportGenericError(pc, bp, sp, addr, true, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_storeN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, true, size, 0, false); } } Index: compiler-rt/trunk/test/asan/TestCases/Darwin/interface_symbols_darwin.c =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Darwin/interface_symbols_darwin.c +++ compiler-rt/trunk/test/asan/TestCases/Darwin/interface_symbols_darwin.c @@ -29,6 +29,18 @@ // RUN: echo __asan_report_store16 >> %t.interface // RUN: echo __asan_report_load_n >> %t.interface // RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_report_load1_noabort >> %t.interface +// RUN: echo __asan_report_load2_noabort >> %t.interface +// RUN: echo __asan_report_load4_noabort >> %t.interface +// RUN: echo __asan_report_load8_noabort >> %t.interface +// RUN: echo __asan_report_load16_noabort >> %t.interface +// RUN: echo __asan_report_store1_noabort >> %t.interface +// RUN: echo __asan_report_store2_noabort >> %t.interface +// RUN: echo __asan_report_store4_noabort >> %t.interface +// RUN: echo __asan_report_store8_noabort >> %t.interface +// RUN: echo __asan_report_store16_noabort >> %t.interface +// RUN: echo __asan_report_load_n_noabort >> %t.interface +// RUN: echo __asan_report_store_n_noabort >> %t.interface // RUN: echo __asan_report_exp_load1 >> %t.interface // RUN: echo __asan_report_exp_load2 >> %t.interface // RUN: echo __asan_report_exp_load4 >> %t.interface Index: compiler-rt/trunk/test/asan/TestCases/Linux/halt_on_error-torture.cc =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Linux/halt_on_error-torture.cc +++ compiler-rt/trunk/test/asan/TestCases/Linux/halt_on_error-torture.cc @@ -0,0 +1,79 @@ +// Stress test recovery mode with many threads. +// +// RUN: %clangxx_asan -fsanitize-recover=address %s -o %t +// +// RUN: env ASAN_OPTIONS=halt_on_error=false:max_errors=1000 %run %t 1 10 >1.txt 2>&1 +// RUN: FileCheck %s < 1.txt +// RUN: [ $(wc -l < 1.txt) -gt 1 ] +// +// RUN: env ASAN_OPTIONS=halt_on_error=false:max_errors=1000 %run %t 10 20 >10.txt 2>&1 +// RUN: FileCheck %s < 10.txt +// This one is racy although very unlikely to fail: +// RUN: [ $(wc -l < 10.txt) -gt 1 ] +// Collisions are highly unlikely but still possible so we need the alternative: +// RUN: FileCheck --check-prefix=CHECK-COLLISION %s < 1.txt || FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt +// +// REQUIRES: stable-runtime + +#include +#include +#include +#include + +#include + +size_t nthreads = 10; +size_t niter = 10; + +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) { + struct timespec delay = { 0, rand_r(&seed) * 1000000 }; + nanosleep(&delay, 0); + + // Expect error collisions here + // CHECK: 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-COLLISION: AddressSanitizer: nested bug in the same thread, aborting + // CHECK-NO-COLLISION: All threads terminated + printf("All threads terminated\n"); + + delete [] tids; + + return 0; +} Index: compiler-rt/trunk/test/asan/TestCases/Linux/interface_symbols_linux.c =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Linux/interface_symbols_linux.c +++ compiler-rt/trunk/test/asan/TestCases/Linux/interface_symbols_linux.c @@ -24,6 +24,18 @@ // RUN: echo __asan_report_store16 >> %t.interface // RUN: echo __asan_report_load_n >> %t.interface // RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_report_load1_noabort >> %t.interface +// RUN: echo __asan_report_load2_noabort >> %t.interface +// RUN: echo __asan_report_load4_noabort >> %t.interface +// RUN: echo __asan_report_load8_noabort >> %t.interface +// RUN: echo __asan_report_load16_noabort >> %t.interface +// RUN: echo __asan_report_store1_noabort >> %t.interface +// RUN: echo __asan_report_store2_noabort >> %t.interface +// RUN: echo __asan_report_store4_noabort >> %t.interface +// RUN: echo __asan_report_store8_noabort >> %t.interface +// RUN: echo __asan_report_store16_noabort >> %t.interface +// RUN: echo __asan_report_load_n_noabort >> %t.interface +// RUN: echo __asan_report_store_n_noabort >> %t.interface // RUN: echo __asan_report_exp_load1 >> %t.interface // RUN: echo __asan_report_exp_load2 >> %t.interface // RUN: echo __asan_report_exp_load4 >> %t.interface Index: compiler-rt/trunk/test/asan/TestCases/halt_on_error-1.c =================================================================== --- compiler-rt/trunk/test/asan/TestCases/halt_on_error-1.c +++ compiler-rt/trunk/test/asan/TestCases/halt_on_error-1.c @@ -0,0 +1,29 @@ +// Test recovery mode. +// +// RUN: %clang_asan -fsanitize-recover=address %s -o %t +// +// RUN: env not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:halt_on_error=true not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:halt_on_error=false %run %t 2>&1 | FileCheck %s --check-prefix CHECK-RECOVER + +#include + +volatile int ten = 10; + +int main() { + char x[10]; + // CHECK: WRITE of size 11 + // CHECK-RECOVER: WRITE of size 11 + memset(x, 0, 11); + // CHECK-NOT: READ of size 1 + // CHECK-RECOVER: READ of size 1 + volatile int res = x[ten]; + // CHECK-NOT: WRITE of size 1 + // CHECK-RECOVER: WRITE of size 1 + x[ten] = res + 3; + // CHECK-NOT: READ of size 1 + // CHECK-RECOVER: READ of size 1 + res = x[ten]; + return 0; +} +