Coverage for /Syzygy/agent/asan/asan_runtime.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
88.4%3523980.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/agent/asan/asan_runtime.h"
  16    :  
  17    :  #include <algorithm>
  18    :  #include <vector>
  19    :  
  20    :  #include "base/bind.h"
  21    :  #include "base/command_line.h"
  22    :  #include "base/environment.h"
  23    :  #include "base/logging.h"
  24    :  #include "base/strings/stringprintf.h"
  25    :  #include "base/strings/sys_string_conversions.h"
  26    :  #include "base/strings/utf_string_conversions.h"
  27    :  #include "base/win/pe_image.h"
  28    :  #include "base/win/wrapped_window_proc.h"
  29    :  #include "syzygy/agent/asan/asan_logger.h"
  30    :  #include "syzygy/agent/asan/block.h"
  31    :  #include "syzygy/agent/asan/heap_checker.h"
  32    :  #include "syzygy/agent/asan/page_protection_helpers.h"
  33    :  #include "syzygy/agent/asan/shadow.h"
  34    :  #include "syzygy/agent/asan/stack_capture_cache.h"
  35    :  #include "syzygy/agent/asan/windows_heap_adapter.h"
  36    :  #include "syzygy/crashdata/crashdata.h"
  37    :  #include "syzygy/trace/client/client_utils.h"
  38    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  39    :  
  40    :  namespace agent {
  41    :  namespace asan {
  42    :  
  43    :  namespace {
  44    :  
  45    :  using agent::asan::AsanLogger;
  46    :  using agent::asan::StackCaptureCache;
  47    :  using agent::asan::WindowsHeapAdapter;
  48    :  using base::win::WinProcExceptionFilter;
  49    :  
  50    :  // Signatures of the various Breakpad functions for setting custom crash
  51    :  // key-value pairs.
  52    :  // Post r194002.
  53    :  typedef void (__cdecl * SetCrashKeyValuePairPtr)(const char*, const char*);
  54    :  // Post r217590.
  55    :  typedef void (__cdecl * SetCrashKeyValueImplPtr)(const wchar_t*,
  56    :                                                   const wchar_t*);
  57    :  
  58    :  // Signature of an enhanced crash reporting function.
  59    :  typedef void(__cdecl* ReportCrashWithProtobufPtr)(EXCEPTION_POINTERS*,
  60    :                                                    const char*,
  61    :                                                    size_t);
  62    :  
  63    :  // Collects the various Breakpad-related exported functions.
  64    :  struct BreakpadFunctions {
  65    :    // The Breakpad crash reporting entry point.
  66    :    WinProcExceptionFilter crash_for_exception_ptr;
  67    :  
  68    :    // The optional enhanced crash reporting entry point.
  69    :    ReportCrashWithProtobufPtr report_crash_with_protobuf_ptr;
  70    :  
  71    :    // Various flavours of the custom key-value setting function. The version
  72    :    // exported depends on the version of Chrome. It is possible for both of these
  73    :    // to be NULL even if crash_for_exception_ptr is not NULL.
  74    :    SetCrashKeyValuePairPtr set_crash_key_value_pair_ptr;
  75    :    SetCrashKeyValueImplPtr set_crash_key_value_impl_ptr;
  76    :  };
  77    :  
  78    :  // The static breakpad functions. All runtimes share these. This is under
  79    :  // AsanRuntime::lock_.
  80    :  BreakpadFunctions breakpad_functions = {};
  81    :  
  82    :  // A custom exception code we use to indicate that the exception originated
  83    :  // from Asan, and shouldn't be processed again by our unhandled exception
  84    :  // handler. This value has been created according to the rules here:
  85    :  // http://msdn.microsoft.com/en-us/library/windows/hardware/ff543026(v=vs.85).aspx
  86    :  // See winerror.h for more details.
  87    :  static const DWORD kAsanFacility = 0x68B;  // No more than 11 bits.
  88    :  static const DWORD kAsanStatus = 0x5AD0;  // No more than 16 bits.
  89    :  static const DWORD kAsanException =
  90    :      (3 << 30) |  // Severity = error.
  91    :      (1 << 29) |  // Customer defined code (not defined by MS).
  92    :      (kAsanFacility << 16) |  // Facility code.
  93    :      kAsanStatus;  // Status code.
  94    :  COMPILE_ASSERT((kAsanFacility >> 11) == 0, too_many_facility_bits);
  95    :  COMPILE_ASSERT((kAsanStatus >> 16) == 0, too_many_status_bits);
  96    :  COMPILE_ASSERT((kAsanException & (3 << 27)) == 0,
  97    :                 bits_27_and_28_must_be_clear);
  98    :  
  99    :  // Raises an exception, first wrapping it an Asan specific exception. This
 100    :  // indicates to our unhandled exception handler that it doesn't need to
 101    :  // process the exception.
 102    :  void RaiseFilteredException(
 103  i :      DWORD code, DWORD flags, DWORD num_args, const ULONG_PTR* args) {
 104    :    // Retain the original arguments and craft a new exception.
 105    :    const ULONG_PTR arguments[4] = {
 106  i :        code, flags, num_args, reinterpret_cast<const ULONG_PTR>(args) };
 107  i :    ::RaiseException(kAsanException, 0, ARRAYSIZE(arguments), arguments);
 108  i :  }
 109    :  
 110    :  // The default error handler. It is expected that this will be bound in a
 111    :  // callback in the Asan runtime.
 112    :  // @param context The context when the error has been reported.
 113    :  // @param error_info The information about this error.
 114  E :  void DefaultErrorHandler(AsanErrorInfo* error_info) {
 115  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
 116    :  
 117    :    ULONG_PTR arguments[] = {
 118    :      reinterpret_cast<ULONG_PTR>(&error_info->context),
 119    :      reinterpret_cast<ULONG_PTR>(error_info)
 120  E :    };
 121    :  
 122  E :    ::DebugBreak();
 123    :  
 124    :    // This raises an error in such a way that the Asan unhandled exception
 125    :    // handler will not process it.
 126    :    RaiseFilteredException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
 127    :                           0,
 128    :                           ARRAYSIZE(arguments),
 129  E :                           arguments);
 130  E :  }
 131    :  
 132    :  // Returns the breakpad crash reporting functions if breakpad is enabled for
 133    :  // the current executable.
 134    :  //
 135    :  // If we're running in the context of a breakpad enabled binary we can
 136    :  // report errors directly via that breakpad entry-point. This allows us
 137    :  // to report the exact context of the error without including the Asan RTL
 138    :  // in crash context, depending on where and when we capture the context.
 139    :  //
 140    :  // @param breakpad_functions The Breakpad functions structure to be populated.
 141    :  // @returns true if we found breakpad functions, false otherwise.
 142  E :  bool GetBreakpadFunctions(BreakpadFunctions* breakpad_functions) {
 143  E :    DCHECK_NE(reinterpret_cast<BreakpadFunctions*>(NULL), breakpad_functions);
 144    :  
 145    :    // Clear the structure.
 146  E :    ::memset(breakpad_functions, 0, sizeof(*breakpad_functions));
 147    :  
 148    :    // The named entry-point exposed to report a crash.
 149    :    static const char kCrashHandlerSymbol[] = "CrashForException";
 150    :  
 151    :    // The optional enhanced entry-point exposed to report a crash.
 152    :    static const char kReportCrashWithProtobufSymbol[] =
 153    :        "ReportCrashWithProtobuf";
 154    :  
 155    :    // The named entry-point exposed to annotate a crash with a key/value pair.
 156    :    static const char kSetCrashKeyValuePairSymbol[] = "SetCrashKeyValuePair";
 157    :    static const char kSetCrashKeyValueImplSymbol[] = "SetCrashKeyValueImpl";
 158    :  
 159    :    // Get a handle to the current executable image.
 160  E :    HMODULE exe_hmodule = ::GetModuleHandle(NULL);
 161    :  
 162    :    // Lookup the crash handler symbol.
 163    :    breakpad_functions->crash_for_exception_ptr =
 164    :        reinterpret_cast<WinProcExceptionFilter>(
 165  E :            ::GetProcAddress(exe_hmodule, kCrashHandlerSymbol));
 166    :  
 167    :    // Lookup the optional enhanced crash handler symbol.
 168    :    breakpad_functions->report_crash_with_protobuf_ptr =
 169    :        reinterpret_cast<ReportCrashWithProtobufPtr>(
 170  E :            ::GetProcAddress(exe_hmodule, kReportCrashWithProtobufSymbol));
 171    :  
 172    :    if (breakpad_functions->crash_for_exception_ptr == NULL &&
 173  E :        breakpad_functions->report_crash_with_protobuf_ptr == NULL) {
 174  E :      return false;
 175    :    }
 176    :  
 177    :    // Lookup the crash annotation symbol.
 178    :    breakpad_functions->set_crash_key_value_pair_ptr =
 179    :        reinterpret_cast<SetCrashKeyValuePairPtr>(
 180  E :            ::GetProcAddress(exe_hmodule, kSetCrashKeyValuePairSymbol));
 181    :    breakpad_functions->set_crash_key_value_impl_ptr =
 182    :        reinterpret_cast<SetCrashKeyValueImplPtr>(
 183  E :            ::GetProcAddress(exe_hmodule, kSetCrashKeyValueImplSymbol));
 184    :  
 185  E :    return true;
 186  E :  }
 187    :  
 188    :  // Sets a crash key using the given breakpad function.
 189    :  void SetCrashKeyValuePair(const BreakpadFunctions& breakpad_functions,
 190    :                            const char* key,
 191  E :                            const char* value) {
 192  E :    if (breakpad_functions.set_crash_key_value_pair_ptr != NULL) {
 193  i :      breakpad_functions.set_crash_key_value_pair_ptr(key, value);
 194  i :      return;
 195    :    }
 196    :  
 197  E :    if (breakpad_functions.set_crash_key_value_impl_ptr != NULL) {
 198  i :      std::wstring wkey = base::UTF8ToWide(key);
 199  i :      std::wstring wvalue = base::UTF8ToWide(value);
 200    :      breakpad_functions.set_crash_key_value_impl_ptr(wkey.c_str(),
 201  i :                                                      wvalue.c_str());
 202  i :      return;
 203    :    }
 204    :  
 205    :    return;
 206  E :  }
 207    :  
 208    :  // Writes the appropriate crash keys for the given error.
 209    :  void SetCrashKeys(const BreakpadFunctions& breakpad_functions,
 210  E :                    AsanErrorInfo* error_info) {
 211    :    DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
 212  E :           breakpad_functions.report_crash_with_protobuf_ptr != NULL);
 213  E :    DCHECK(error_info != NULL);
 214    :  
 215    :    SetCrashKeyValuePair(breakpad_functions,
 216    :                         "asan-error-type",
 217  E :                         ErrorInfoAccessTypeToStr(error_info->error_type));
 218    :  
 219  E :    if (error_info->shadow_info[0] != '\0') {
 220    :      SetCrashKeyValuePair(breakpad_functions,
 221    :                           "asan-error-message",
 222  E :                           error_info->shadow_info);
 223    :    }
 224  E :  }
 225    :  
 226    :  // Initializes an exception record for an Asan crash.
 227    :  void InitializeExceptionRecord(const AsanErrorInfo* error_info,
 228    :                                 EXCEPTION_RECORD* record,
 229  E :                                 EXCEPTION_POINTERS* pointers) {
 230  E :    DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), error_info);
 231  E :    DCHECK_NE(static_cast<EXCEPTION_RECORD*>(nullptr), record);
 232  E :    DCHECK_NE(static_cast<EXCEPTION_POINTERS*>(nullptr), pointers);
 233    :  
 234  E :    ::memset(record, 0, sizeof(EXCEPTION_RECORD));
 235  E :    record->ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
 236    :    record->ExceptionAddress = reinterpret_cast<PVOID>(
 237  E :        error_info->context.Eip);
 238  E :    record->NumberParameters = 2;
 239    :    record->ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(
 240  E :        &error_info->context);
 241  E :    record->ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
 242    :  
 243  E :    pointers->ExceptionRecord = record;
 244  E :    pointers->ContextRecord = const_cast<CONTEXT*>(&error_info->context);
 245  E :  }
 246    :  
 247    :  // Creates a serialized protobuf representing crash data.
 248  E :  bool PopulateProtobuf(const AsanErrorInfo& error_info, std::string* protobuf) {
 249  E :    DCHECK_NE(static_cast<std::string*>(nullptr), protobuf);
 250  E :    crashdata::Value value;
 251  E :    PopulateErrorInfo(error_info, &value);
 252  E :    if (!value.SerializeToString(protobuf))
 253  i :      return false;
 254  E :    return true;
 255  E :  }
 256    :  
 257    :  // The breakpad error handler. It is expected that this will be bound in a
 258    :  // callback in the Asan runtime.
 259    :  // @param breakpad_functions A struct containing pointers to the various
 260    :  //     Breakpad reporting functions.
 261    :  // @param error_info The information about this error.
 262    :  void BreakpadErrorHandler(const BreakpadFunctions& breakpad_functions,
 263  E :                            AsanErrorInfo* error_info) {
 264    :    DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
 265  E :           breakpad_functions.report_crash_with_protobuf_ptr != NULL);
 266  E :    DCHECK(error_info != NULL);
 267    :  
 268  E :    SetCrashKeys(breakpad_functions, error_info);
 269    :  
 270  E :    EXCEPTION_RECORD exception = {};
 271  E :    EXCEPTION_POINTERS pointers = {};
 272  E :    InitializeExceptionRecord(error_info, &exception, &pointers);
 273    :  
 274  E :    if (breakpad_functions.report_crash_with_protobuf_ptr) {
 275  E :      std::string protobuf;
 276  E :      PopulateProtobuf(*error_info, &protobuf);
 277    :      breakpad_functions.report_crash_with_protobuf_ptr(
 278  E :          &pointers, protobuf.data(), protobuf.length());
 279  E :    } else {
 280  E :      breakpad_functions.crash_for_exception_ptr(&pointers);
 281    :    }
 282  i :    NOTREACHED();
 283  i :  }
 284    :  
 285    :  // A helper function to find if an intrusive list contains a given entry.
 286    :  // @param list The list in which we want to look for the entry.
 287    :  // @param item The entry we want to look for.
 288    :  // @returns true if the list contains this entry, false otherwise.
 289    :  bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
 290    :    LIST_ENTRY* current = list->Flink;
 291    :    while (current != NULL) {
 292    :      LIST_ENTRY* next_item = NULL;
 293    :      if (current->Flink != list) {
 294    :        next_item = current->Flink;
 295    :      }
 296    :  
 297    :      if (current == item) {
 298    :        return true;
 299    :      }
 300    :  
 301    :      current = next_item;
 302    :    }
 303    :    return false;
 304    :  }
 305    :  
 306    :  // Check if the current process is large address aware.
 307    :  // @returns true if it is, false otherwise.
 308  E :  bool CurrentProcessIsLargeAddressAware() {
 309  E :    const base::win::PEImage image(::GetModuleHandle(NULL));
 310    :  
 311    :    bool process_is_large_address_aware =
 312    :      (image.GetNTHeaders()->FileHeader.Characteristics &
 313  E :          IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
 314    :  
 315  E :    return process_is_large_address_aware;
 316  E :  }
 317    :  
 318    :  // A helper function to send a command to Windbg. Windbg should first receive
 319    :  // the ".ocommand ASAN" command to treat those messages as commands.
 320  E :  void AsanDbgCmd(const wchar_t* fmt, ...) {
 321  E :    if (!base::debug::BeingDebugged())
 322  E :      return;
 323    :    // The string should start with "ASAN" to be interpreted by the debugger as a
 324    :    // command.
 325  i :    std::wstring command_wstring = L"ASAN ";
 326    :    va_list args;
 327  i :    va_start(args, fmt);
 328    :  
 329    :    // Append the actual command to the wstring.
 330  i :    base::StringAppendV(&command_wstring, fmt, args);
 331    :  
 332    :    // Append "; g" to make sure that the debugger continues its execution after
 333    :    // executing this command. This is needed because when the .ocommand function
 334    :    // is used under Windbg the debugger will break on OutputDebugString.
 335  i :    command_wstring.append(L"; g");
 336    :  
 337  i :    OutputDebugString(command_wstring.c_str());
 338  E :  }
 339    :  
 340    :  // A helper function to print a message to Windbg's console.
 341  E :  void AsanDbgMessage(const wchar_t* fmt, ...) {
 342  E :    if (!base::debug::BeingDebugged())
 343  E :      return;
 344    :    // Prepend the message with the .echo command so it'll be printed into the
 345    :    // debugger's console.
 346  i :    std::wstring message_wstring = L".echo ";
 347    :    va_list args;
 348  i :    va_start(args, fmt);
 349    :  
 350    :    // Append the actual message to the wstring.
 351  i :    base::StringAppendV(&message_wstring, fmt, args);
 352    :  
 353    :    // Treat the message as a command to print it.
 354  i :    AsanDbgCmd(message_wstring.c_str());
 355  E :  }
 356    :  
 357    :  // Switch to the caller's context and print its stack trace in Windbg.
 358    :  void AsanDbgPrintContext(const CONTEXT& context) {
 359    :    if (!base::debug::BeingDebugged())
 360    :      return;
 361    :    AsanDbgMessage(L"Caller's context (%p) and stack trace:", &context);
 362    :    AsanDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
 363    :  }
 364    :  
 365    :  // Returns the maximum allocation size that can be made safely. This leaves
 366    :  // space for child function frames, ideally enough for Breakpad to do its
 367    :  // work.
 368  E :  size_t MaxSafeAllocaSize() {
 369    :    // We leave 5KB of stack space for Breakpad and other crash reporting
 370    :    // machinery.
 371  E :    const size_t kReservedStack = 5 * 1024;
 372    :  
 373    :    // Find the base of the stack.
 374  E :    MEMORY_BASIC_INFORMATION mbi = {};
 375  E :    void* stack = &mbi;
 376  E :    if (VirtualQuery(stack, &mbi, sizeof(mbi)) == 0)
 377  i :      return 0;
 378    :    size_t max_size = reinterpret_cast<uint8*>(stack) -
 379  E :        reinterpret_cast<uint8*>(mbi.AllocationBase);
 380  E :    max_size -= std::min(max_size, kReservedStack);
 381  E :    return max_size;
 382  E :  }
 383    :  
 384    :  // Performs a dynamic stack allocation of at most |size| bytes. Sets the actual
 385    :  // size of the allocation and the pointer to it by modifying |size| and |result|
 386    :  // directly.
 387    :  #define SAFE_ALLOCA(size, result) {  \
 388    :      size_t max_size = MaxSafeAllocaSize();  \
 389    :      size = std::min(size, max_size);  \
 390    :      result = _alloca(size);  \
 391    :      if (result == NULL)  \
 392    :        size = 0;  \
 393    :    }
 394    :  
 395    :  // Runs the heap checker if enabled. If heap corruption is found serializes
 396    :  // the results to the stack and modifies the |error_info| structure.
 397    :  #define CHECK_HEAP_CORRUPTION(runtime, error_info)  \
 398    :    (error_info)->heap_is_corrupt = false;  \
 399    :    if (!((runtime)->params_.check_heap_on_failure)) {  \
 400    :      runtime_->logger_->Write(  \
 401    :            "SyzyASAN: Heap checker disabled, ignoring exception.");  \
 402    :    } else {  \
 403    :      runtime_->logger_->Write(  \
 404    :            "SyzyASAN: Heap checker enabled, processing exception.");  \
 405    :      AutoHeapManagerLock lock(runtime_->heap_manager_.get());  \
 406    :      HeapChecker heap_checker;  \
 407    :      HeapChecker::CorruptRangesVector corrupt_ranges;  \
 408    :      heap_checker.IsHeapCorrupt(&corrupt_ranges);  \
 409    :      size_t size = (runtime)->CalculateCorruptHeapInfoSize(corrupt_ranges);  \
 410    :      void* buffer = NULL;  \
 411    :      if (size > 0) {  \
 412    :        SAFE_ALLOCA(size, buffer);  \
 413    :        (runtime)->WriteCorruptHeapInfo(  \
 414    :            corrupt_ranges, size, buffer, error_info);  \
 415    :      }  \
 416    :    }
 417    :  
 418    :  }  // namespace
 419    :  
 420  E :  base::Lock AsanRuntime::lock_;
 421    :  AsanRuntime* AsanRuntime::runtime_ = NULL;
 422    :  LPTOP_LEVEL_EXCEPTION_FILTER AsanRuntime::previous_uef_ = NULL;
 423    :  bool AsanRuntime::uef_installed_ = false;
 424    :  
 425    :  AsanRuntime::AsanRuntime()
 426  E :      : logger_(), stack_cache_(), asan_error_callback_(), heap_manager_() {
 427  E :    ::common::SetDefaultAsanParameters(&params_);
 428  E :    starting_ticks_ = ::GetTickCount();
 429  E :  }
 430    :  
 431  E :  AsanRuntime::~AsanRuntime() {
 432  E :  }
 433    :  
 434  E :  void AsanRuntime::SetUp(const std::wstring& flags_command_line) {
 435  E :    base::AutoLock auto_lock(lock_);
 436  E :    DCHECK(!runtime_);
 437  E :    runtime_ = this;
 438    :  
 439    :    // Ensure that the current process is not large address aware. It shouldn't be
 440    :    // because the shadow memory assume that the process will only be able to use
 441    :    // 2GB of address space.
 442  E :    CHECK(!CurrentProcessIsLargeAddressAware());
 443    :  
 444    :    // Initialize the command-line structures. This is needed so that
 445    :    // SetUpLogger() can include the command-line in the message announcing
 446    :    // this process. Note: this is mostly for debugging purposes.
 447  E :    CommandLine::Init(0, NULL);
 448    :  
 449  E :    Shadow::SetUp();
 450    :  
 451    :    // Setup the "global" state.
 452  E :    common::StackCapture::Init();
 453  E :    StackCaptureCache::Init();
 454  E :    SetUpLogger();
 455  E :    SetUpStackCache();
 456  E :    SetUpHeapManager();
 457  E :    WindowsHeapAdapter::SetUp(heap_manager_.get());
 458    :  
 459    :    // Parse and propagate any flags set via the environment variable. This logs
 460    :    // failure for us.
 461  E :    if (!::common::ParseAsanParameters(flags_command_line, &params_))
 462  i :      return;
 463    :  
 464    :    // Propagates the flags values to the different modules.
 465  E :    PropagateParams();
 466    :  
 467    :    // Register the error reporting callback to use if/when an Asan error is
 468    :    // detected. If we're able to resolve a breakpad error reporting function
 469    :    // then use that; otherwise, fall back to the default error handler.
 470    :    if (!params_.disable_breakpad_reporting &&
 471  E :        GetBreakpadFunctions(&breakpad_functions)) {
 472  E :      logger_->Write("SyzyASAN: Using Breakpad for error reporting.");
 473  E :      SetErrorCallBack(base::Bind(&BreakpadErrorHandler, breakpad_functions));
 474  E :    } else {
 475  E :      logger_->Write("SyzyASAN: Using default error reporting handler.");
 476  E :      SetErrorCallBack(base::Bind(&DefaultErrorHandler));
 477    :    }
 478    :  
 479    :    // Install the unhandled exception handler. This is only installed once
 480    :    // across all runtime instances in a process so we check that it hasn't
 481    :    // already been installed.
 482  E :    if (!uef_installed_) {
 483  E :      uef_installed_ = true;
 484  E :      previous_uef_ = ::SetUnhandledExceptionFilter(&UnhandledExceptionFilter);
 485    :    }
 486    :  
 487    :    // Finally, initialize the heap manager. This comes after parsing all
 488    :    // parameters as some decisions can only be made once.
 489  E :    heap_manager_->Init();
 490  E :  }
 491    :  
 492  E :  void AsanRuntime::TearDown() {
 493  E :    base::AutoLock auto_lock(lock_);
 494    :  
 495  E :    WindowsHeapAdapter::TearDown();
 496  E :    TearDownHeapManager();
 497  E :    TearDownStackCache();
 498  E :    TearDownLogger();
 499  E :    DCHECK(asan_error_callback_.is_null() == FALSE);
 500  E :    asan_error_callback_.Reset();
 501  E :    Shadow::TearDown();
 502    :  
 503    :    // Unregister ourselves as the singleton runtime for UEF.
 504  E :    runtime_ = NULL;
 505    :  
 506    :    // In principle, we should also check that all the heaps have been destroyed
 507    :    // but this is not guaranteed to be the case in Chrome, so the heap list may
 508    :    // not be empty here.
 509  E :  }
 510    :  
 511  E :  void AsanRuntime::OnErrorImpl(AsanErrorInfo* error_info) {
 512  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
 513    :  
 514  E :    LogAsanErrorInfo(error_info);
 515    :  
 516  E :    if (params_.minidump_on_failure) {
 517  E :      DCHECK(logger_.get() != NULL);
 518  E :      logger_->SaveMiniDump(&error_info->context, error_info);
 519    :    }
 520    :  
 521  E :    if (params_.exit_on_failure) {
 522  E :      DCHECK(logger_.get() != NULL);
 523  E :      logger_->Stop();
 524  E :      exit(EXIT_FAILURE);
 525    :    }
 526  E :  }
 527    :  
 528  E :  void AsanRuntime::OnError(AsanErrorInfo* error_info) {
 529  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
 530    :  
 531    :    // Grab the global page protection lock to prevent page protection settings
 532    :    // from being modified while processing the error.
 533  E :    ::common::AutoRecursiveLock lock(block_protect_lock);
 534    :  
 535    :    // Unfortunately this is a giant macro, but it needs to be as it performs
 536    :    // stack allocations.
 537  E :    CHECK_HEAP_CORRUPTION(this, error_info);
 538    :  
 539  E :    OnErrorImpl(error_info);
 540    :  
 541    :    // Call the callback to handle this error.
 542  E :    DCHECK(!asan_error_callback_.is_null());
 543  E :    asan_error_callback_.Run(error_info);
 544  E :  }
 545    :  
 546  E :  void AsanRuntime::SetErrorCallBack(const AsanOnErrorCallBack& callback) {
 547  E :    asan_error_callback_ = callback;
 548  E :  }
 549    :  
 550  E :  void AsanRuntime::SetUpLogger() {
 551    :    // Setup variables we're going to use.
 552  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 553  E :    scoped_ptr<AsanLogger> client(new AsanLogger);
 554  E :    CHECK(env.get() != NULL);
 555  E :    CHECK(client.get() != NULL);
 556    :  
 557    :    // Initialize the client.
 558    :    client->set_instance_id(
 559  E :        base::UTF8ToWide(trace::client::GetInstanceIdForThisModule()));
 560  E :    client->Init();
 561    :  
 562    :    // Register the client singleton instance.
 563  E :    logger_.reset(client.release());
 564  E :  }
 565    :  
 566  E :  void AsanRuntime::TearDownLogger() {
 567  E :    logger_.reset();
 568  E :  }
 569    :  
 570  E :  void AsanRuntime::SetUpStackCache() {
 571  E :    DCHECK(stack_cache_.get() == NULL);
 572  E :    DCHECK(logger_.get() != NULL);
 573  E :    stack_cache_.reset(new StackCaptureCache(logger_.get()));
 574  E :  }
 575    :  
 576  E :  void AsanRuntime::TearDownStackCache() {
 577  E :    DCHECK(stack_cache_.get() != NULL);
 578  E :    stack_cache_->LogStatistics();
 579  E :    stack_cache_.reset();
 580  E :  }
 581    :  
 582  E :  void AsanRuntime::SetUpHeapManager() {
 583    :    DCHECK_EQ(static_cast<heap_managers::BlockHeapManager*>(NULL),
 584  E :              heap_manager_.get());
 585  E :    DCHECK_NE(static_cast<StackCaptureCache*>(NULL), stack_cache_.get());
 586  E :    heap_manager_.reset(new heap_managers::BlockHeapManager(stack_cache_.get()));
 587    :  
 588    :    // Configure the heap manager to notify us on heap corruption.
 589    :    heap_manager_->SetHeapErrorCallback(base::Bind(&AsanRuntime::OnError,
 590  E :                                                   base::Unretained(this)));
 591  E :  }
 592    :  
 593  E :  void AsanRuntime::TearDownHeapManager() {
 594    :    DCHECK_NE(static_cast<heap_managers::BlockHeapManager*>(NULL),
 595  E :              heap_manager_.get());
 596    :    // Tear down the heap manager before we destroy it and lose our pointer
 597    :    // to it. This is necessary because the heap manager can raise errors
 598    :    // while tearing down the heap, which will in turn call back into the
 599    :    // block heap manager via the runtime.
 600  E :    heap_manager_->TearDownHeapManager();
 601  E :    heap_manager_.reset();
 602  E :  }
 603    :  
 604  E :  bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
 605  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 606  E :    if (env.get() == NULL) {
 607  i :      LOG(ERROR) << "base::Environment::Create returned NULL.";
 608  i :      return false;
 609    :    }
 610    :  
 611    :    // If this fails, the environment variable simply does not exist.
 612  E :    std::string env_var_str;
 613  E :    if (!env->GetVar(::common::kSyzyAsanOptionsEnvVar, &env_var_str)) {
 614  E :      return true;
 615    :    }
 616    :  
 617  E :    *env_var_wstr = base::SysUTF8ToWide(env_var_str);
 618    :  
 619  E :    return true;
 620  E :  }
 621    :  
 622  E :  void AsanRuntime::PropagateParams() {
 623    :    // This function has to be kept in sync with the AsanParameters struct. These
 624    :    // checks will ensure that this is the case.
 625    :    COMPILE_ASSERT(sizeof(::common::AsanParameters) == 56,
 626    :                   must_update_propagate_params);
 627    :    COMPILE_ASSERT(::common::kAsanParametersVersion == 8,
 628    :                   must_update_parameters_version);
 629    :  
 630    :    // Push the configured parameter values to the appropriate endpoints.
 631  E :    heap_manager_->set_parameters(params_);
 632    :    StackCaptureCache::set_compression_reporting_period(
 633  E :        params_.reporting_period);
 634    :    common::StackCapture::set_bottom_frames_to_skip(
 635  E :        params_.bottom_frames_to_skip);
 636  E :    stack_cache_->set_max_num_frames(params_.max_num_frames);
 637    :    // ignored_stack_ids is used locally by AsanRuntime.
 638  E :    logger_->set_log_as_text(params_.log_as_text);
 639    :    // exit_on_failure is used locally by AsanRuntime.
 640  E :    logger_->set_minidump_on_failure(params_.minidump_on_failure);
 641  E :  }
 642    :  
 643    :  size_t AsanRuntime::CalculateCorruptHeapInfoSize(
 644  E :      const HeapChecker::CorruptRangesVector& corrupt_ranges) {
 645    :    size_t n = corrupt_ranges.size() *
 646  E :        (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
 647  E :    return n;
 648  E :  }
 649    :  
 650    :  void AsanRuntime::WriteCorruptHeapInfo(
 651    :      const HeapChecker::CorruptRangesVector& corrupt_ranges,
 652    :      size_t buffer_size,
 653    :      void* buffer,
 654  E :      AsanErrorInfo* error_info) {
 655    :    DCHECK((buffer_size == 0 && buffer == NULL) ||
 656  E :           (buffer_size != 0 && buffer != NULL));
 657  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
 658    :  
 659  E :    ::memset(buffer, 0, buffer_size);
 660    :  
 661  E :    error_info->heap_is_corrupt = false;
 662  E :    error_info->corrupt_range_count = 0;
 663  E :    error_info->corrupt_block_count = 0;
 664  E :    error_info->corrupt_ranges_reported = 0;
 665  E :    error_info->corrupt_ranges = NULL;
 666    :  
 667  E :    if (corrupt_ranges.empty())
 668  i :      return;
 669    :  
 670    :    // If we have corrupt ranges then set the aggregate fields.
 671  E :    error_info->heap_is_corrupt = true;
 672  E :    error_info->corrupt_range_count = corrupt_ranges.size();
 673  E :    for (size_t i = 0; i < corrupt_ranges.size(); ++i)
 674  E :      error_info->corrupt_block_count += corrupt_ranges[i].block_count;
 675    :  
 676    :    // We report a AsanCorruptBlockRange and at least one AsanBlockInfo per
 677    :    // corrupt range. Determine how many ranges we can report on.
 678    :    size_t range_count = buffer_size /
 679  E :        (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
 680  E :    range_count = std::min(range_count, corrupt_ranges.size());
 681    :  
 682    :    // Allocate space for the corrupt range metadata.
 683  E :    uint8* cursor = reinterpret_cast<uint8*>(buffer);
 684  E :    uint8* buffer_end = cursor + buffer_size;
 685    :    error_info->corrupt_ranges = reinterpret_cast<AsanCorruptBlockRange*>(
 686  E :        cursor);
 687  E :    cursor += range_count * sizeof(AsanCorruptBlockRange);
 688  E :    error_info->corrupt_range_count = corrupt_ranges.size();
 689  E :    error_info->corrupt_ranges_reported = range_count;
 690    :  
 691    :    // Allocate space for the corrupt block metadata.
 692  E :    size_t block_count = (buffer_end - cursor) / sizeof(AsanBlockInfo);
 693  E :    AsanBlockInfo* block_infos = reinterpret_cast<AsanBlockInfo*>(cursor);
 694  E :    cursor += block_count * sizeof(AsanBlockInfo);
 695    :  
 696    :    // Write as many corrupt block ranges as we have room for. This is
 697    :    // effectively random as it is by order of address.
 698  E :    for (size_t i = 0; i < range_count; ++i) {
 699    :      // Copy the information about the corrupt range.
 700  E :      error_info->corrupt_ranges[i] = corrupt_ranges[i];
 701    :  
 702    :      // Allocate space for the first block of this range on the stack.
 703    :      // TODO(sebmarchand): Report more blocks if necessary.
 704  E :      AsanBlockInfo* asan_block_info = block_infos;
 705  E :      error_info->corrupt_ranges[i].block_info = block_infos;
 706  E :      error_info->corrupt_ranges[i].block_info_count = 1;
 707  E :      ++block_infos;
 708    :  
 709    :      // Use a shadow walker to find the first corrupt block in this range and
 710    :      // copy its metadata.
 711    :      ShadowWalker shadow_walker(
 712    :          false,
 713    :          reinterpret_cast<const uint8*>(corrupt_ranges[i].address),
 714    :          reinterpret_cast<const uint8*>(corrupt_ranges[i].address) +
 715  E :              corrupt_ranges[i].length);
 716  E :      BlockInfo block_info = {};
 717  E :      CHECK(shadow_walker.Next(&block_info));
 718    :      // The heap checker removes block protections as it goes, so this block
 719    :      // should be readable. However, remove page protections just to be sure.
 720    :      // They are left turned off so that the minidump generation can introspect
 721    :      // the block.
 722  E :      BlockProtectNone(block_info);
 723  E :      ErrorInfoGetAsanBlockInfo(block_info, stack_cache_.get(), asan_block_info);
 724  E :      DCHECK_EQ(kDataIsCorrupt, asan_block_info->analysis.block_state);
 725  E :    }
 726    :  
 727    :    return;
 728  E :  }
 729    :  
 730  E :  void AsanRuntime::LogAsanErrorInfo(AsanErrorInfo* error_info) {
 731  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
 732    :  
 733  E :    const char* bug_descr = ErrorInfoAccessTypeToStr(error_info->error_type);
 734  E :    if (logger_->log_as_text()) {
 735    :      std::string output(base::StringPrintf(
 736    :          "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n",
 737  E :          bug_descr, error_info->location, error_info->crash_stack_id));
 738  E :      if (error_info->access_mode != agent::asan::ASAN_UNKNOWN_ACCESS) {
 739  E :        const char* access_mode_str = NULL;
 740  E :        if (error_info->access_mode == agent::asan::ASAN_READ_ACCESS)
 741  E :          access_mode_str = "READ";
 742  E :        else
 743  E :          access_mode_str = "WRITE";
 744    :        base::StringAppendF(&output,
 745    :                            "%s of size %d at 0x%08X\n",
 746    :                            access_mode_str,
 747    :                            error_info->access_size,
 748  E :                            error_info->location);
 749    :      }
 750    :  
 751    :      // Log the failure and stack.
 752  E :      logger_->WriteWithContext(output, error_info->context);
 753    :  
 754  E :      logger_->Write(error_info->shadow_info);
 755  E :      if (error_info->block_info.free_stack_size != 0U) {
 756    :        logger_->WriteWithStackTrace("freed here:\n",
 757    :                                     error_info->block_info.free_stack,
 758  E :                                     error_info->block_info.free_stack_size);
 759    :      }
 760  E :      if (error_info->block_info.alloc_stack_size != NULL) {
 761    :        logger_->WriteWithStackTrace("previously allocated here:\n",
 762    :                                     error_info->block_info.alloc_stack,
 763  E :                                     error_info->block_info.alloc_stack_size);
 764    :      }
 765  E :      if (error_info->error_type >= USE_AFTER_FREE) {
 766  E :        std::string shadow_text;
 767  E :        Shadow::AppendShadowMemoryText(error_info->location, &shadow_text);
 768  E :        logger_->Write(shadow_text);
 769  E :      }
 770  E :    }
 771    :  
 772    :    // Print the base of the Windbg help message.
 773    :    AsanDbgMessage(L"An Asan error has been found (%ls), here are the details:",
 774  E :                   base::SysUTF8ToWide(bug_descr).c_str());
 775    :  
 776    :    // Print the Windbg information to display the allocation stack if present.
 777  E :    if (error_info->block_info.alloc_stack_size != NULL) {
 778  E :      AsanDbgMessage(L"Allocation stack trace:");
 779    :      AsanDbgCmd(L"dps %p l%d",
 780    :                 error_info->block_info.alloc_stack,
 781  E :                 error_info->block_info.alloc_stack_size);
 782    :    }
 783    :  
 784    :    // Print the Windbg information to display the free stack if present.
 785  E :    if (error_info->block_info.free_stack_size != NULL) {
 786  E :      AsanDbgMessage(L"Free stack trace:");
 787    :      AsanDbgCmd(L"dps %p l%d",
 788    :                 error_info->block_info.free_stack,
 789  E :                 error_info->block_info.free_stack_size);
 790    :    }
 791  E :  }
 792    :  
 793  E :  void AsanRuntime::GetBadAccessInformation(AsanErrorInfo* error_info) {
 794  E :    base::AutoLock lock(lock_);
 795    :  
 796    :    // Checks if this is an access to an internal structure or if it's an access
 797    :    // in the upper region of the memory (over the 2 GB limit).
 798    :    if ((reinterpret_cast<size_t>(error_info->location) & (1 << 31)) != 0 ||
 799    :        Shadow::GetShadowMarkerForAddress(error_info->location)
 800  E :            == kAsanMemoryMarker) {
 801  E :        error_info->error_type = WILD_ACCESS;
 802  E :    } else if (Shadow::GetShadowMarkerForAddress(error_info->location) ==
 803  E :        kInvalidAddressMarker) {
 804  i :      error_info->error_type = INVALID_ADDRESS;
 805  i :    } else {
 806  E :      ErrorInfoGetBadAccessInformation(stack_cache_.get(), error_info);
 807    :    }
 808  E :  }
 809    :  
 810  E :  bool AsanRuntime::allocation_filter_flag() {
 811  E :    return heap_manager_->allocation_filter_flag();
 812  E :  }
 813    :  
 814  E :  void AsanRuntime::set_allocation_filter_flag(bool value) {
 815  E :    heap_manager_->set_allocation_filter_flag(value);
 816  E :  }
 817    :  
 818  E :  void AsanRuntime::AddThreadId(uint32 thread_id) {
 819  E :    DCHECK_NE(0u, thread_id);
 820  E :    base::AutoLock lock(thread_ids_lock_);
 821  E :    thread_ids_.insert(thread_id);
 822  E :  }
 823    :  
 824  E :  bool AsanRuntime::ThreadIdIsValid(uint32 thread_id) {
 825  E :    base::AutoLock lock(thread_ids_lock_);
 826  E :    return thread_ids_.count(thread_id) > 0;
 827  E :  }
 828    :  
 829  E :  bool AsanRuntime::HeapIdIsValid(HeapManagerInterface::HeapId heap_id) {
 830    :    // Consider dying heaps in this query, as they are still valid from the
 831    :    // point of view of an error report.
 832  E :    return heap_manager_->IsValidHeapIdUnlocked(heap_id, true);
 833  E :  }
 834    :  
 835  E :  HeapType AsanRuntime::GetHeapType(HeapManagerInterface::HeapId heap_id) {
 836  E :    return heap_manager_->GetHeapTypeUnlocked(heap_id);
 837  E :  }
 838    :  
 839  i :  int AsanRuntime::CrashForException(EXCEPTION_POINTERS* exception) {
 840  i :    return ExceptionFilterImpl(false, exception);
 841  i :  }
 842    :  
 843    :  LONG WINAPI AsanRuntime::UnhandledExceptionFilter(
 844  E :      struct _EXCEPTION_POINTERS* exception) {
 845  E :    return ExceptionFilterImpl(true, exception);
 846  E :  }
 847    :  
 848    :  LONG AsanRuntime::ExceptionFilterImpl(bool is_unhandled,
 849  E :                                        EXCEPTION_POINTERS* exception) {
 850    :    // This ensures that we don't have multiple colliding crashes being processed
 851    :    // simultaneously.
 852  E :    base::AutoLock auto_lock(lock_);
 853    :  
 854    :    // Grab the global page protection lock to prevent page protection settings
 855    :    // from being modified while processing the error.
 856  E :    ::common::AutoRecursiveLock lock(block_protect_lock);
 857    :  
 858    :    // This is needed for unittesting.
 859  E :    runtime_->logger_->Write("SyzyASAN: Handling an exception.");
 860    :  
 861    :    // If we're bound to a runtime then look for heap corruption and
 862    :    // potentially augment the exception record. This needs to exist in the
 863    :    // outermost scope of this function as pointers to it may be passed to
 864    :    // other exception handlers.
 865  E :    AsanErrorInfo error_info = {};
 866    :  
 867    :    // If this is set to true then an Asan error will be emitted.
 868  E :    bool emit_asan_error = false;
 869    :  
 870    :    // If this is an exception that we launched then extract the original
 871    :    // exception data and continue processing it.
 872  E :    if (exception->ExceptionRecord->ExceptionCode == kAsanException) {
 873  i :      ULONG_PTR* args = exception->ExceptionRecord->ExceptionInformation;
 874  i :      DWORD code = args[0];
 875  i :      DWORD flags = args[1];
 876  i :      DWORD nargs = args[2];
 877  i :      const ULONG_PTR* orig_args = reinterpret_cast<const ULONG_PTR*>(args[3]);
 878    :  
 879    :      // Rebuild the exception with the original exception data.
 880  i :      exception->ExceptionRecord->ExceptionCode = code;
 881  i :      exception->ExceptionRecord->ExceptionFlags = flags;
 882  i :      exception->ExceptionRecord->NumberParameters = nargs;
 883  i :      for (DWORD i = 0; i < nargs; ++i)
 884  i :        args[i] = orig_args[i];
 885  E :    } else if (runtime_) {
 886    :      // Initialize this as if heap corruption is the primary error being
 887    :      // reported. This will be overridden by the access violation handling
 888    :      // code below, if necessary.
 889  E :      error_info.location = exception->ExceptionRecord->ExceptionAddress;
 890  E :      error_info.context = *exception->ContextRecord;
 891  E :      error_info.error_type = CORRUPT_HEAP;
 892  E :      error_info.access_mode = ASAN_UNKNOWN_ACCESS;
 893    :  
 894    :      // It is possible that access violations are due to page protections of a
 895    :      // sufficiently large allocation. In this case the shadow will contain
 896    :      // block redzone markers at the given address. We take over the exception
 897    :      // if that is the case.
 898    :      if (exception->ExceptionRecord->ExceptionCode ==
 899    :              EXCEPTION_ACCESS_VIOLATION &&
 900    :          exception->ExceptionRecord->NumberParameters >= 2 &&
 901  E :          exception->ExceptionRecord->ExceptionInformation[0] <= 1) {
 902    :        void* address = reinterpret_cast<void*>(
 903  E :            exception->ExceptionRecord->ExceptionInformation[1]);
 904  E :        ShadowMarker marker = Shadow::GetShadowMarkerForAddress(address);
 905    :        if (ShadowMarkerHelper::IsRedzone(marker) &&
 906  E :            ShadowMarkerHelper::IsActiveBlock(marker)) {
 907  E :          BlockInfo block_info = {};
 908  E :          if (Shadow::BlockInfoFromShadow(address, &block_info)) {
 909    :            // Page protections have to be removed from this block otherwise our
 910    :            // own inspection will cause further errors.
 911  E :            ScopedBlockAccess block_access(block_info);
 912    :  
 913    :            // Useful for unittesting.
 914    :            runtime_->logger_->Write("SyzyASAN: Caught an invalid access via "
 915  E :                "an access violation exception.");
 916    :  
 917    :            // Override the invalid access location with the faulting address,
 918    :            // not the code address.
 919  E :            error_info.location = address;
 920    :            // The exact access size isn't reported so simply set it to 1 (an
 921    :            // obvious lower bound).
 922  E :            error_info.access_size = 1;
 923    :            // Determine if this is a read or a write using information in the
 924    :            // exception record.
 925    :            error_info.access_mode =
 926    :                exception->ExceptionRecord->ExceptionInformation[0] == 0 ?
 927  E :                ASAN_READ_ACCESS : ASAN_WRITE_ACCESS;
 928    :  
 929    :            // Fill out the rest of the bad access information.
 930    :            ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
 931  E :                                             &error_info);
 932  E :            emit_asan_error = true;
 933  E :          }
 934    :        }
 935    :      }
 936    :  
 937  E :      CHECK_HEAP_CORRUPTION(runtime_, &error_info);
 938  E :      if (error_info.heap_is_corrupt)
 939  E :        emit_asan_error = true;
 940    :    }
 941    :  
 942    :    // If an Asan error was detected then report it via the logger and take over
 943    :    // the exception record.
 944  E :    EXCEPTION_RECORD record = {};
 945  E :    if (emit_asan_error) {
 946    :      // Log the error via the usual means.
 947  E :      runtime_->OnErrorImpl(&error_info);
 948    :  
 949    :      // If we have Breakpad integration then set our crash keys.
 950  E :      if (breakpad_functions.crash_for_exception_ptr != NULL)
 951  E :        SetCrashKeys(breakpad_functions, &error_info);
 952    :  
 953    :      // Remember the old exception record.
 954  E :      EXCEPTION_RECORD* old_record = exception->ExceptionRecord;
 955    :  
 956    :      // Initialize the exception record and chain the original exception to it.
 957  E :      InitializeExceptionRecord(&error_info, &record, exception);
 958  E :      record.ExceptionRecord = old_record;
 959    :    }
 960    :  
 961  E :    if (breakpad_functions.report_crash_with_protobuf_ptr) {
 962    :      // This method is expected to terminate the process.
 963  E :      std::string protobuf;
 964  E :      PopulateProtobuf(error_info, &protobuf);
 965    :      breakpad_functions.report_crash_with_protobuf_ptr(
 966  E :          exception, protobuf.data(), protobuf.length());
 967  E :      return EXCEPTION_CONTINUE_SEARCH;
 968    :    }
 969    :  
 970  E :    if (is_unhandled) {
 971    :      // Pass the buck to the next exception handler. If the process is Breakpad
 972    :      // enabled this will eventually make its way there.
 973  E :      if (previous_uef_ != NULL)
 974  E :        return (*previous_uef_)(exception);
 975    :    }
 976    :  
 977    :    // If we've found an Asan error then pass the buck to Breakpad directly,
 978    :    // if possible. Otherwise, simply let things take their natural course.
 979  i :    if (emit_asan_error && breakpad_functions.crash_for_exception_ptr)
 980  i :      return (*breakpad_functions.crash_for_exception_ptr)(exception);
 981    :  
 982    :    // We can't do anything with this, so let the system deal with it.
 983  i :    return EXCEPTION_CONTINUE_SEARCH;
 984  i :  }
 985    :  
 986    :  }  // namespace asan
 987    :  }  // namespace agent

Coverage information generated Thu Mar 26 16:15:41 2015.