Index: lib/asan/asan_flags.inc =================================================================== --- lib/asan/asan_flags.inc +++ 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: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -69,7 +69,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: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ 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: lib/asan/asan_report.h =================================================================== --- lib/asan/asan_report.h +++ 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 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 NORETURN -ReportODRViolation(const __asan_global *g1, u32 stack_id1, - const __asan_global *g2, u32 stack_id2); +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: lib/asan/asan_report.cc =================================================================== --- lib/asan/asan_report.cc +++ lib/asan/asan_report.cc @@ -621,41 +621,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. @@ -666,11 +681,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; @@ -687,7 +730,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( @@ -958,13 +1001,8 @@ DescribeHeapAddress(addr, 1); } -} // 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) { +void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, + uptr access_size, u32 exp, bool fatal) { ENABLE_FRAME_POINTER; // Optimization experiments. @@ -1033,7 +1071,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()); @@ -1059,6 +1097,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: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -112,13 +112,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) @@ -131,22 +136,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); \ @@ -158,7 +168,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); \ } \ } \ } @@ -166,12 +177,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) @@ -189,7 +204,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); } } @@ -198,7 +213,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); } } @@ -207,7 +231,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); } } @@ -216,7 +240,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: test/asan/TestCases/Darwin/interface_symbols_darwin.c =================================================================== --- test/asan/TestCases/Darwin/interface_symbols_darwin.c +++ 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: test/asan/TestCases/Linux/halt_on_error-torture.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Linux/halt_on_error-torture.cc @@ -0,0 +1,73 @@ +// 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: cat 1.txt | FileCheck %s +// RUN: [ $(cat 1.txt | wc -l) -gt 1 ] +// +// RUN: env ASAN_OPTIONS=halt_on_error=false:max_errors=1000 %run %t 10 20 >10.txt 2>&1 +// RUN: cat 10.txt | FileCheck %s +// RUN: [ $(cat 10.txt | wc -l) -gt 1 ] + +#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: All threads terminated + printf("All threads terminated\n"); + + delete [] tids; + + return 0; +} Index: test/asan/TestCases/Linux/interface_symbols_linux.c =================================================================== --- test/asan/TestCases/Linux/interface_symbols_linux.c +++ 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: test/asan/TestCases/halt_on_error-1.c =================================================================== --- /dev/null +++ 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; +} +