[ASan] Enable optional ASan recovery.
Differential Revision: http://reviews.llvm.org/D12318 llvm-svn: 252723
This commit is contained in:
		
							parent
							
								
									12982a816c
								
							
						
					
					
						commit
						468d955b98
					
				| 
						 | 
				
			
			@ -134,3 +134,6 @@ ASAN_FLAG(int, detect_odr_violation, 2,
 | 
			
		|||
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!)")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ struct AsanInterceptorContext {
 | 
			
		|||
      }                                                                 \
 | 
			
		||||
      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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,6 +167,19 @@ extern "C" {
 | 
			
		|||
  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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -622,41 +622,56 @@ void DescribeThread(AsanThreadContext *context) {
 | 
			
		|||
// 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 @@ class ScopedInErrorReport {
 | 
			
		|||
    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 ReportStackOverflow(const SignalContext &sig) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
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 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
 | 
			
		|||
 | 
			
		||||
  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 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
 | 
			
		|||
  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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,45 +49,41 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
 | 
			
		|||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,13 +113,18 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
 | 
			
		|||
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, 4)
 | 
			
		|||
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<u8 *>(sp)          \
 | 
			
		||||
                                        : *reinterpret_cast<u16 *>(sp);        \
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +169,8 @@ ASAN_REPORT_ERROR_N(store, true)
 | 
			
		|||
          *__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 @@ ASAN_REPORT_ERROR_N(store, true)
 | 
			
		|||
#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 @@ NOINLINE INTERFACE_ATTRIBUTE
 | 
			
		|||
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 @@ NOINLINE INTERFACE_ATTRIBUTE
 | 
			
		|||
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 @@ NOINLINE INTERFACE_ATTRIBUTE
 | 
			
		|||
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 @@ NOINLINE INTERFACE_ATTRIBUTE
 | 
			
		|||
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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include <sanitizer/asan_interface.h>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <string.h>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue