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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
78.1%2393060.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 "base/bind.h"
  18    :  #include "base/command_line.h"
  19    :  #include "base/environment.h"
  20    :  #include "base/logging.h"
  21    :  #include "base/string_number_conversions.h"
  22    :  #include "base/string_util.h"
  23    :  #include "base/stringprintf.h"
  24    :  #include "base/utf_string_conversions.h"
  25    :  #include "base/strings/string_tokenizer.h"
  26    :  #include "base/strings/sys_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/asan_shadow.h"
  31    :  #include "syzygy/agent/asan/stack_capture_cache.h"
  32    :  #include "syzygy/trace/client/client_utils.h"
  33    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  34    :  
  35    :  namespace agent {
  36    :  namespace asan {
  37    :  
  38    :  namespace {
  39    :  
  40    :  using agent::asan::AsanLogger;
  41    :  using agent::asan::HeapProxy;
  42    :  using agent::asan::StackCaptureCache;
  43    :  using base::win::WinProcExceptionFilter;
  44    :  
  45    :  // Signatures of the various Breakpad functions for setting custom crash
  46    :  // key-value pairs.
  47    :  // Post r194002.
  48    :  typedef void (__cdecl * SetCrashKeyValuePairPtr)(const char*, const char*);
  49    :  // Post r217590.
  50    :  typedef void (__cdecl * SetCrashKeyValueImplPtr)(const wchar_t*,
  51    :                                                   const wchar_t*);
  52    :  
  53    :  // Collects the various Breakpad-related exported functions.
  54    :  struct BreakpadFunctions {
  55    :    // The Breakpad crash reporting entry point.
  56    :    WinProcExceptionFilter crash_for_exception_ptr;
  57    :  
  58    :    // Various flavours of the custom key-value setting function. The version
  59    :    // exported depends on the version of Chrome. It is possible for both of these
  60    :    // to be NULL even if crash_for_exception_ptr is not NULL.
  61    :    SetCrashKeyValuePairPtr set_crash_key_value_pair_ptr;
  62    :    SetCrashKeyValueImplPtr set_crash_key_value_impl_ptr;
  63    :  };
  64    :  
  65    :  // The default error handler. It is expected that this will be bound in a
  66    :  // callback in the ASAN runtime.
  67    :  // @param context The context when the error has been reported.
  68    :  // @param error_info The information about this error.
  69  i :  void DefaultErrorHandler(AsanErrorInfo* error_info) {
  70  i :    DCHECK(error_info != NULL);
  71    :  
  72    :    ULONG_PTR arguments[] = {
  73  i :      reinterpret_cast<ULONG_PTR>(&error_info->context),
  74    :      reinterpret_cast<ULONG_PTR>(error_info)
  75  i :    };
  76    :  
  77  i :    ::DebugBreak();
  78    :    ::RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
  79    :                     0,
  80    :                     ARRAYSIZE(arguments),
  81  i :                     &arguments[0]);
  82  i :  }
  83    :  
  84    :  // Returns the breakpad crash reporting functions if breakpad is enabled for
  85    :  // the current executable.
  86    :  //
  87    :  // If we're running in the context of a breakpad enabled binary we can
  88    :  // report errors directly via that breakpad entry-point. This allows us
  89    :  // to report the exact context of the error without including the ASan RTL
  90    :  // in crash context, depending on where and when we capture the context.
  91    :  //
  92    :  // @param breakpad_functions The Breakpad functions structure to be populated.
  93    :  // @returns true if we found breakpad functions, false otherwise.
  94  E :  bool GetBreakpadFunctions(BreakpadFunctions* breakpad_functions) {
  95  E :    DCHECK(breakpad_functions != NULL);
  96    :  
  97    :    // Clear the structure.
  98  E :    ::memset(breakpad_functions, 0, sizeof(*breakpad_functions));
  99    :  
 100    :    // The named entry-point exposed to report a crash.
 101    :    static const char kCrashHandlerSymbol[] = "CrashForException";
 102    :  
 103    :    // The named entry-point exposed to annotate a crash with a key/value pair.
 104    :    static const char kSetCrashKeyValuePairSymbol[] = "SetCrashKeyValuePair";
 105    :    static const char kSetCrashKeyValueImplSymbol[] = "SetCrashKeyValueImpl";
 106    :  
 107    :    // Get a handle to the current executable image.
 108  E :    HMODULE exe_hmodule = ::GetModuleHandle(NULL);
 109    :  
 110    :    // Lookup the crash handler symbol.
 111    :    breakpad_functions->crash_for_exception_ptr =
 112    :        reinterpret_cast<WinProcExceptionFilter>(
 113  E :            ::GetProcAddress(exe_hmodule, kCrashHandlerSymbol));
 114  E :    if (breakpad_functions->crash_for_exception_ptr == NULL)
 115  E :      return false;
 116    :  
 117    :    // Lookup the crash annotation symbol.
 118    :    breakpad_functions->set_crash_key_value_pair_ptr =
 119    :        reinterpret_cast<SetCrashKeyValuePairPtr>(
 120  i :            ::GetProcAddress(exe_hmodule, kSetCrashKeyValuePairSymbol));
 121    :    breakpad_functions->set_crash_key_value_impl_ptr =
 122    :        reinterpret_cast<SetCrashKeyValueImplPtr>(
 123  i :            ::GetProcAddress(exe_hmodule, kSetCrashKeyValueImplSymbol));
 124    :  
 125  i :    return true;
 126  E :  }
 127    :  
 128    :  // Sets a crash key using the given breakpad function.
 129    :  void SetCrashKeyValuePair(const BreakpadFunctions& breakpad_functions,
 130    :                            const char* key,
 131  E :                            const char* value) {
 132  E :    if (breakpad_functions.set_crash_key_value_pair_ptr != NULL) {
 133  i :      breakpad_functions.set_crash_key_value_pair_ptr(key, value);
 134  i :      return;
 135    :    }
 136    :  
 137  E :    if (breakpad_functions.set_crash_key_value_impl_ptr != NULL) {
 138  i :      std::wstring wkey = UTF8ToWide(key);
 139  i :      std::wstring wvalue = UTF8ToWide(value);
 140    :      breakpad_functions.set_crash_key_value_impl_ptr(wkey.c_str(),
 141  i :                                                      wvalue.c_str());
 142  i :      return;
 143    :    }
 144    :  
 145    :    return;
 146  E :  }
 147    :  
 148    :  // The breakpad error handler. It is expected that this will be bound in a
 149    :  // callback in the ASAN runtime.
 150    :  // @param breakpad_functions A struct containing pointers to the various
 151    :  //     Breakpad reporting functions.
 152    :  // @param error_info The information about this error.
 153    :  void BreakpadErrorHandler(const BreakpadFunctions& breakpad_functions,
 154  i :                            AsanErrorInfo* error_info) {
 155  i :    DCHECK(breakpad_functions.crash_for_exception_ptr != NULL);
 156  i :    DCHECK(error_info != NULL);
 157    :  
 158    :    SetCrashKeyValuePair(breakpad_functions,
 159    :                         "asan-error-type",
 160  i :                         HeapProxy::AccessTypeToStr(error_info->error_type));
 161    :  
 162  i :    if (error_info->shadow_info[0] != '\0') {
 163    :      SetCrashKeyValuePair(breakpad_functions,
 164    :                           "asan-error-message",
 165  i :                           error_info->shadow_info);
 166    :    }
 167    :  
 168  i :    EXCEPTION_RECORD exception = {};
 169  i :    exception.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
 170    :    exception.ExceptionAddress = reinterpret_cast<PVOID>(
 171  i :        error_info->context.Eip);
 172  i :    exception.NumberParameters = 2;
 173    :    exception.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(
 174  i :        &error_info->context);
 175  i :    exception.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
 176    :  
 177  i :    EXCEPTION_POINTERS pointers = { &exception, &error_info->context };
 178  i :    breakpad_functions.crash_for_exception_ptr(&pointers);
 179  i :    NOTREACHED();
 180  i :  }
 181    :  
 182    :  // Trinary return values used to indicate if a flag was updated or not.
 183    :  enum FlagResult {
 184    :    kFlagNotPresent,
 185    :    kFlagSet,
 186    :    kFlagError
 187    :  };
 188    :  
 189    :  // Try to update the value of a size_t variable from a command-line.
 190    :  // @param cmd_line The command line who might contain a given parameter.
 191    :  // @param param_name The parameter that we want to read.
 192    :  // @param value Will receive the value of the parameter if it's present.
 193    :  // @returns kFlagNotPresent if the flag was not present and left at its default;
 194    :  //     kFlagSet if the flag was present, valid and modified; or
 195    :  //     kFlagError if the flag was present but invalid.
 196    :  FlagResult UpdateSizetFromCommandLine(const CommandLine& cmd_line,
 197    :                                        const std::string& param_name,
 198  E :                                        size_t* value) {
 199  E :    DCHECK(value != NULL);
 200  E :    if (!cmd_line.HasSwitch(param_name))
 201  E :      return kFlagNotPresent;
 202  E :    std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
 203  E :    size_t new_value = 0;
 204  E :    if (!base::StringToSizeT(value_str, &new_value))
 205  i :      return kFlagError;
 206  E :    *value = new_value;
 207    :  
 208  E :    return kFlagSet;
 209  E :  }
 210    :  
 211    :  // Try to update the value of an array of ignored stack ids from a command-line.
 212    :  // We expect the values to be in hexadecimal format and separated by a
 213    :  // semi-colon.
 214    :  // @param cmd_line The command line to parse.
 215    :  // @param param_name The parameter that we want to read.
 216    :  // @param values Will receive the set of parsed values.
 217    :  // @returns true on success, false otherwise.
 218    :  bool ReadIgnoredStackIdsFromCommandLine(const CommandLine& cmd_line,
 219    :                                          const std::string& param_name,
 220  E :                                          AsanRuntime::StackIdSet* values) {
 221  E :    DCHECK(values != NULL);
 222  E :    if (!cmd_line.HasSwitch(param_name))
 223  E :      return true;
 224  E :    std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
 225  E :    base::StringTokenizer string_tokenizer(value_str, ";");
 226  E :    while (string_tokenizer.GetNext()) {
 227  E :      int64 new_value = 0;
 228  E :      if (!base::HexStringToInt64(string_tokenizer.token(), &new_value))
 229  i :        return false;
 230  E :      values->insert(static_cast<StackCapture::StackId>(new_value));
 231  E :    }
 232  E :    return true;
 233  E :  }
 234    :  
 235    :  // A helper function to find if an intrusive list contains a given entry.
 236    :  // @param list The list in which we want to look for the entry.
 237    :  // @param item The entry we want to look for.
 238    :  // @returns true if the list contains this entry, false otherwise.
 239  E :  bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
 240  E :    LIST_ENTRY* current = list->Flink;
 241  E :    while (current != NULL) {
 242  E :      LIST_ENTRY* next_item = NULL;
 243  E :      if (current->Flink != list) {
 244  E :        next_item = current->Flink;
 245    :      }
 246    :  
 247  E :      if (current == item) {
 248  E :        return true;
 249    :      }
 250    :  
 251  E :      current = next_item;
 252  E :    }
 253  i :    return false;
 254  E :  }
 255    :  
 256    :  // Check if the current process is large address aware.
 257    :  // @returns true if it is, false otherwise.
 258  E :  bool CurrentProcessIsLargeAddressAware() {
 259  E :    const base::win::PEImage image(::GetModuleHandle(NULL));
 260    :  
 261    :    bool process_is_large_address_aware =
 262    :      (image.GetNTHeaders()->FileHeader.Characteristics &
 263  E :          IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
 264    :  
 265  E :    return process_is_large_address_aware;
 266  E :  }
 267    :  
 268    :  // A helper function to send a command to Windbg. Windbg should first receive
 269    :  // the ".ocommand ASAN" command to treat those messages as commands.
 270  E :  void ASANDbgCmd(const wchar_t* fmt, ...) {
 271  E :    if (!base::debug::BeingDebugged())
 272  E :      return;
 273    :    // The string should start with "ASAN" to be interpreted by the debugger as a
 274    :    // command.
 275  i :    std::wstring command_wstring = L"ASAN ";
 276    :    va_list args;
 277  i :    va_start(args, fmt);
 278    :  
 279    :    // Append the actual command to the wstring.
 280  i :    base::StringAppendV(&command_wstring, fmt, args);
 281    :  
 282    :    // Append "; g" to make sure that the debugger continues its execution after
 283    :    // executing this command. This is needed because when the .ocommand function
 284    :    // is used under Windbg the debugger will break on OutputDebugString.
 285  i :    command_wstring.append(L"; g");
 286    :  
 287  i :    OutputDebugString(command_wstring.c_str());
 288  E :  }
 289    :  
 290    :  // A helper function to print a message to Windbg's console.
 291  E :  void ASANDbgMessage(const wchar_t* fmt, ...) {
 292  E :    if (!base::debug::BeingDebugged())
 293  E :      return;
 294    :    // Prepend the message with the .echo command so it'll be printed into the
 295    :    // debugger's console.
 296  i :    std::wstring message_wstring = L".echo ";
 297    :    va_list args;
 298  i :    va_start(args, fmt);
 299    :  
 300    :    // Append the actual message to the wstring.
 301  i :    base::StringAppendV(&message_wstring, fmt, args);
 302    :  
 303    :    // Treat the message as a command to print it.
 304  i :    ASANDbgCmd(message_wstring.c_str());
 305  E :  }
 306    :  
 307    :  // Switch to the caller's context and print its stack trace in Windbg.
 308    :  void ASANDbgPrintContext(const CONTEXT& context) {
 309    :    if (!base::debug::BeingDebugged())
 310    :      return;
 311    :    ASANDbgMessage(L"Caller's context (%p) and stack trace:", &context);
 312    :    ASANDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
 313    :  }
 314    :  
 315    :  // Experiment groups.
 316    :  const size_t kExperimentQuarantineSizes[] = {
 317    :    8 * 1024 * 1024,
 318    :    16 * 1024 * 1024,  // This is our current default.
 319    :    32 * 1024 * 1024,
 320    :    64 * 1024 * 1024 };
 321    :  // Average allocation size is 140 bytes, so each of these has an estimated
 322    :  // memory process overhead. The header/footer already account for 36 bytes.
 323    :  const size_t kExperimentTrailerPaddingSizes[] = {
 324    :    0,   // 36 byte red zone (25.7% overhead). This is our current default.
 325    :    12,  // 48 byte red zone (34.3% overhead).
 326    :    28,  // 64 byte red zone (45.7% overhead).
 327    :    92   // 128 byte red zone (91.4% overhead).
 328    :  };
 329    :  
 330    :  // Gets the value of a coin toss, which is used for putting us into experimental
 331    :  // groups. We get this value by checking for a SYZYGY_ASAN_COIN_TOSS environment
 332    :  // variable. If the variable does not exist or is malformed, we consider that
 333    :  // the client is opted out of experiments. Otherwise, they are opted in and the
 334    :  // coin toss value (an unsigned 64 bit integer expressed in hex) is returned.
 335    :  // @param value Will be populated with the coin toss value on success, 0
 336    :  //     otherwise.
 337    :  // @returns true if the client is opted in, false otherwise.
 338  E :  bool GetSyzygyAsanCoinToss(uint64* value) {
 339  E :    DCHECK_NE(reinterpret_cast<uint64*>(NULL), value);
 340    :  
 341  E :    *value = 0;
 342    :  
 343  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 344  E :    if (env.get() == NULL)
 345  i :      return false;
 346    :  
 347  E :    std::string s;
 348  E :    if (!env->GetVar(AsanRuntime::kSyzygyAsanCoinTossEnvVar, &s))
 349  E :      return false;
 350    :  
 351  E :    if (!base::HexStringToUInt64(s, value))
 352  E :      return false;
 353    :  
 354  E :    return true;
 355  E :  }
 356    :  
 357    :  }  // namespace
 358    :  
 359    :  const char AsanRuntime::kSyzygyAsanCoinTossEnvVar[] = "SYZYGY_ASAN_COIN_TOSS";
 360    :  const char AsanRuntime::kSyzygyAsanOptionsEnvVar[] = "SYZYGY_ASAN_OPTIONS";
 361    :  
 362    :  const char AsanRuntime::kBottomFramesToSkip[] = "bottom_frames_to_skip";
 363    :  const char AsanRuntime::kCompressionReportingPeriod[] =
 364    :      "compression_reporting_period";
 365    :  const char AsanRuntime::kExitOnFailure[] = "exit_on_failure";
 366    :  const char AsanRuntime::kIgnoredStackIds[] = "ignored_stack_ids";
 367    :  const char AsanRuntime::kMaxNumberOfFrames[] = "max_num_frames";
 368    :  const char AsanRuntime::kMiniDumpOnFailure[] = "minidump_on_failure";
 369    :  const char AsanRuntime::kNoLogAsText[] = "no_log_as_text";
 370    :  const char AsanRuntime::kQuarantineSize[] = "quarantine_size";
 371    :  const wchar_t AsanRuntime::kSyzyAsanDll[] = L"syzyasan_rtl.dll";
 372    :  const char AsanRuntime::kTrailerPaddingSize[] = "trailer_padding_size";
 373    :  
 374    :  AsanRuntime::AsanRuntime()
 375    :      : logger_(NULL), stack_cache_(NULL), asan_error_callback_(), flags_(),
 376  E :        heap_proxy_dlist_lock_(), heap_proxy_dlist_() {
 377  E :  }
 378    :  
 379  E :  AsanRuntime::~AsanRuntime() {
 380  E :  }
 381    :  
 382  E :  void AsanRuntime::SetUp(const std::wstring& flags_command_line) {
 383    :    // Ensure that the current process is not large address aware. It shouldn't be
 384    :    // because the shadow memory assume that the process will only be able to use
 385    :    // 2GB of address space.
 386  E :    CHECK(!CurrentProcessIsLargeAddressAware());
 387    :  
 388    :    // Initialize the command-line structures. This is needed so that
 389    :    // SetUpLogger() can include the command-line in the message announcing
 390    :    // this process. Note: this is mostly for debugging purposes.
 391  E :    CommandLine::Init(0, NULL);
 392    :  
 393  E :    Shadow::SetUp();
 394    :  
 395  E :    InitializeListHead(&heap_proxy_dlist_);
 396    :  
 397    :    // Setup the "global" state.
 398  E :    StackCapture::Init();
 399  E :    StackCaptureCache::Init();
 400  E :    SetUpLogger();
 401  E :    SetUpStackCache();
 402  E :    HeapProxy::Init(stack_cache_.get());
 403  E :    if (!ParseFlagsFromString(flags_command_line)) {
 404  i :      LOG(ERROR) << "Unable to parse the flags from the input string (\""
 405    :                 << flags_command_line.c_str() << "\").";
 406    :    }
 407    :  
 408    :    // Propagates the flags values to the different modules.
 409  E :    PropagateFlagsValues();
 410    :  
 411    :    // Register the error reporting callback to use if/when an ASAN error is
 412    :    // detected. If we're able to resolve a breakpad error reporting function
 413    :    // then use that; otherwise, fall back to the default error handler.
 414  E :    BreakpadFunctions breakpad_functions = {};
 415  E :    if (GetBreakpadFunctions(&breakpad_functions)) {
 416  i :      LOG(INFO) << "SyzyASAN: Using Breakpad for error reporting.";
 417  i :      SetErrorCallBack(base::Bind(&BreakpadErrorHandler, breakpad_functions));
 418  i :    } else {
 419  E :      LOG(INFO) << "SyzyASAN: Using default error reporting handler.";
 420  E :      SetErrorCallBack(base::Bind(&DefaultErrorHandler));
 421    :    }
 422    :  
 423    :    // Reporting of the experiment group. This is also reported via Finch/UMA, but
 424    :    // we duplicate it to the crash keys for ease of filtering.
 425  E :    if (flags_.opted_in) {
 426    :      SetCrashKeyValuePair(
 427    :          breakpad_functions,
 428    :          "asan-experiment-quarantine-size",
 429  E :          base::UintToString(flags_.quarantine_size).c_str());
 430    :      SetCrashKeyValuePair(
 431    :          breakpad_functions,
 432    :          "asan-experiment-trailer-padding-size",
 433  E :          base::UintToString(flags_.trailer_padding_size).c_str());
 434    :    }
 435  E :  }
 436    :  
 437  E :  void AsanRuntime::TearDown() {
 438  E :    TearDownStackCache();
 439  E :    TearDownLogger();
 440  E :    DCHECK(asan_error_callback_.is_null() == FALSE);
 441  E :    asan_error_callback_.Reset();
 442  E :    Shadow::TearDown();
 443    :    // In principle, we should also check that all the heaps have been destroyed
 444    :    // but this is not guaranteed to be the case in Chrome, so the heap list may
 445    :    // not be empty here.
 446  E :  }
 447    :  
 448  E :  void AsanRuntime::OnError(AsanErrorInfo* error_info) {
 449  E :    DCHECK(error_info != NULL);
 450    :  
 451    :    const char* bug_descr =
 452  E :        HeapProxy::AccessTypeToStr(error_info->error_type);
 453  E :    if (logger_->log_as_text()) {
 454    :      std::string output(base::StringPrintf(
 455    :          "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n",
 456  E :          bug_descr, error_info->location, error_info->crash_stack_id));
 457  E :      if (error_info->access_mode != HeapProxy::ASAN_UNKNOWN_ACCESS) {
 458  E :        const char* access_mode_str = NULL;
 459  E :        if (error_info->access_mode == HeapProxy::ASAN_READ_ACCESS)
 460  E :          access_mode_str = "READ";
 461  E :        else
 462  E :          access_mode_str = "WRITE";
 463    :        base::StringAppendF(&output,
 464    :                            "%s of size %d at 0x%08X\n",
 465    :                            access_mode_str,
 466  E :                            error_info->access_size);
 467    :      }
 468    :  
 469    :      // Log the failure and stack.
 470  E :      logger_->WriteWithContext(output, error_info->context);
 471    :  
 472  E :      logger_->Write(error_info->shadow_info);
 473  E :      if (error_info->free_stack_size != 0U) {
 474    :        logger_->WriteWithStackTrace("freed here:\n",
 475    :                                     error_info->free_stack,
 476  E :                                     error_info->free_stack_size);
 477    :      }
 478  E :      if (error_info->alloc_stack_size != NULL) {
 479    :        logger_->WriteWithStackTrace("previously allocated here:\n",
 480    :                                     error_info->alloc_stack,
 481  E :                                     error_info->alloc_stack_size);
 482    :      }
 483  E :      if (error_info->error_type >= HeapProxy::USE_AFTER_FREE) {
 484  E :        std::string shadow_text;
 485  E :        Shadow::AppendShadowMemoryText(error_info->location, &shadow_text);
 486  E :        logger_->Write(shadow_text);
 487  E :      }
 488  E :    }
 489    :  
 490    :    // Print the base of the Windbg help message.
 491    :    ASANDbgMessage(L"An Asan error has been found (%ls), here are the details:",
 492  E :                   base::SysUTF8ToWide(bug_descr).c_str());
 493    :  
 494    :    // Print the Windbg information to display the allocation stack if present.
 495  E :    if (error_info->alloc_stack_size != NULL) {
 496  E :      ASANDbgMessage(L"Allocation stack trace:");
 497    :      ASANDbgCmd(L"dps %p l%d",
 498    :                 error_info->alloc_stack,
 499  E :                 error_info->alloc_stack_size);
 500    :    }
 501    :  
 502    :    // Print the Windbg information to display the free stack if present.
 503  E :    if (error_info->free_stack_size != NULL) {
 504  E :      ASANDbgMessage(L"Free stack trace:");
 505    :      ASANDbgCmd(L"dps %p l%d",
 506    :                 error_info->free_stack,
 507  E :                 error_info->free_stack_size);
 508    :    }
 509    :  
 510  E :    if (flags_.minidump_on_failure) {
 511  i :      DCHECK(logger_.get() != NULL);
 512  i :      logger_->SaveMiniDump(&error_info->context, error_info);
 513    :    }
 514    :  
 515  E :    if (flags_.exit_on_failure) {
 516  E :      DCHECK(logger_.get() != NULL);
 517  E :      logger_->Stop();
 518  E :      exit(EXIT_FAILURE);
 519    :    }
 520    :  
 521    :    // Call the callback to handle this error.
 522  E :    DCHECK(!asan_error_callback_.is_null());
 523  E :    asan_error_callback_.Run(error_info);
 524  E :  }
 525    :  
 526  E :  void AsanRuntime::SetErrorCallBack(const AsanOnErrorCallBack& callback) {
 527  E :    asan_error_callback_ = callback;
 528  E :  }
 529    :  
 530  E :  void AsanRuntime::SetUpLogger() {
 531    :    // Setup variables we're going to use.
 532  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 533  E :    scoped_ptr<AsanLogger> client(new AsanLogger);
 534  E :    CHECK(env.get() != NULL);
 535  E :    CHECK(client.get() != NULL);
 536    :  
 537    :    // Initialize the client.
 538    :    client->set_instance_id(
 539  E :        UTF8ToWide(trace::client::GetInstanceIdForThisModule()));
 540  E :    client->Init();
 541    :  
 542    :    // Register the client singleton instance.
 543  E :    logger_.reset(client.release());
 544  E :  }
 545    :  
 546  E :  void AsanRuntime::TearDownLogger() {
 547  E :    logger_.reset();
 548  E :  }
 549    :  
 550  E :  void AsanRuntime::SetUpStackCache() {
 551  E :    DCHECK(stack_cache_.get() == NULL);
 552  E :    DCHECK(logger_.get() != NULL);
 553  E :    stack_cache_.reset(new StackCaptureCache(logger_.get()));
 554  E :  }
 555    :  
 556  E :  void AsanRuntime::TearDownStackCache() {
 557  E :    DCHECK(stack_cache_.get() != NULL);
 558  E :    stack_cache_->LogStatistics();
 559  E :    stack_cache_.reset();
 560  E :  }
 561    :  
 562  E :  bool AsanRuntime::ParseFlagsFromString(std::wstring str) {
 563    :    // Prepends the flags with the agent name. We need to do this because the
 564    :    // command-line constructor expect the process name to be the first value of
 565    :    // the command-line string.
 566    :    // Start by inserting a space at the beginning of the flags to separate the
 567    :    // flags from the agent name.
 568  E :    str.insert(0, L" ");
 569    :    // Insert the agent name.
 570  E :    str.insert(0, kSyzyAsanDll);
 571    :  
 572  E :    CommandLine cmd_line = CommandLine::FromString(str);
 573    :  
 574    :    // Get our experiment status.
 575  E :    flags_.opted_in = GetSyzygyAsanCoinToss(&flags_.coin_toss);
 576  E :    uint64 coin_toss = flags_.coin_toss;
 577    :  
 578    :    // Parse the quarantine size flag.
 579  E :    flags_.quarantine_size = HeapProxy::default_quarantine_max_size();
 580    :    FlagResult flag_result = UpdateSizetFromCommandLine(
 581  E :        cmd_line, kQuarantineSize, &flags_.quarantine_size);
 582  E :    if (flag_result == kFlagError) {
 583  i :      LOG(ERROR) << "Unable to read " << kQuarantineSize << " from the argument "
 584    :                 << "list.";
 585  i :      return false;
 586  E :    } else if (flag_result == kFlagNotPresent && flags_.opted_in) {
 587  E :      size_t n = arraysize(kExperimentQuarantineSizes);
 588  E :      flags_.quarantine_size = kExperimentQuarantineSizes[coin_toss % n];
 589  E :      coin_toss /= n;
 590  E :      LOG(INFO) << "Using experiment quarantine size of "
 591    :                << flags_.quarantine_size << ".";
 592    :    }
 593    :  
 594    :    // Parse the trailer padding size flag.
 595  E :    flags_.trailer_padding_size = 0;
 596    :    flag_result = UpdateSizetFromCommandLine(
 597  E :        cmd_line, kTrailerPaddingSize, &flags_.trailer_padding_size);
 598  E :    if (flag_result == kFlagError) {
 599  i :      LOG(ERROR) << "Unable to read " << kTrailerPaddingSize << " from the "
 600    :                 << "argument list.";
 601  i :      return false;
 602  E :    } else if (flag_result == kFlagNotPresent && flags_.opted_in) {
 603  E :      size_t n = arraysize(kExperimentTrailerPaddingSizes);
 604  E :      flags_.trailer_padding_size = kExperimentTrailerPaddingSizes[coin_toss % n];
 605  E :      coin_toss /= n;
 606  E :      LOG(INFO) << "Using experiment trailer padding size of "
 607    :                << flags_.trailer_padding_size << ".";
 608    :    }
 609    :  
 610    :    // Parse the reporting period flag.
 611    :    flags_.reporting_period =
 612  E :        StackCaptureCache::GetDefaultCompressionReportingPeriod();
 613    :    if (UpdateSizetFromCommandLine(cmd_line, kCompressionReportingPeriod,
 614  E :                                   &flags_.reporting_period) == kFlagError) {
 615  i :      LOG(ERROR) << "Unable to read " << kCompressionReportingPeriod
 616    :                 << " from the argument list.";
 617  i :      return false;
 618    :    }
 619    :  
 620    :    // Parse the bottom frames to skip flag.
 621  E :    flags_.bottom_frames_to_skip = StackCapture::bottom_frames_to_skip();
 622    :    if (UpdateSizetFromCommandLine(cmd_line, kBottomFramesToSkip,
 623  E :                                   &flags_.bottom_frames_to_skip) == kFlagError) {
 624  i :      LOG(ERROR) << "Unable to read " << kBottomFramesToSkip << " from the "
 625    :                 << "argument list.";
 626  i :      return false;
 627    :    }
 628    :  
 629    :    // Parse the max number of frames flag.
 630  E :    flags_.max_num_frames = stack_cache_->max_num_frames();
 631    :    if (UpdateSizetFromCommandLine(cmd_line, kMaxNumberOfFrames,
 632  E :                                   &flags_.max_num_frames) == kFlagError) {
 633  i :      LOG(ERROR) << "Unable to read " << kMaxNumberOfFrames << " from the "
 634    :                 << "argument list.";
 635  i :      return false;
 636    :    }
 637    :  
 638    :    // Parse the ignored stack ids.
 639    :    if (!ReadIgnoredStackIdsFromCommandLine(cmd_line, kIgnoredStackIds,
 640  E :                                            &flags_.ignored_stack_ids)) {
 641  i :      LOG(ERROR) << "Unable to read " << kIgnoredStackIds << " from the "
 642    :                 << "argument list.";
 643  i :      return false;
 644    :    }
 645    :  
 646    :    // Parse the other (boolean) flags.
 647  E :    flags_.exit_on_failure = cmd_line.HasSwitch(kExitOnFailure);
 648  E :    flags_.minidump_on_failure = cmd_line.HasSwitch(kMiniDumpOnFailure);
 649  E :    flags_.log_as_text = !cmd_line.HasSwitch(kNoLogAsText);
 650    :  
 651  E :    return true;
 652  E :  }
 653    :  
 654  E :  bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
 655  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 656  E :    if (env.get() == NULL) {
 657  i :      LOG(ERROR) << "base::Environment::Create returned NULL.";
 658  i :      return false;
 659    :    }
 660    :  
 661    :    // If this fails, the environment variable simply does not exist.
 662  E :    std::string env_var_str;
 663  E :    if (!env->GetVar(kSyzygyAsanOptionsEnvVar, &env_var_str)) {
 664  E :      return true;
 665    :    }
 666    :  
 667  i :    *env_var_wstr = base::SysUTF8ToWide(env_var_str);
 668    :  
 669  i :    return true;
 670  E :  }
 671    :  
 672  E :  void AsanRuntime::PropagateFlagsValues() const {
 673    :    // TODO(sebmarchand): Look into edit-free ways to expose new flags to the
 674    :    //     different modules.
 675  E :    HeapProxy::set_trailer_padding_size(flags_.trailer_padding_size);
 676  E :    HeapProxy::set_default_quarantine_max_size(flags_.quarantine_size);
 677  E :    StackCapture::set_bottom_frames_to_skip(flags_.bottom_frames_to_skip);
 678  E :    StackCaptureCache::set_compression_reporting_period(flags_.reporting_period);
 679  E :    stack_cache_->set_max_num_frames(flags_.max_num_frames);
 680  E :    logger_->set_log_as_text(flags_.log_as_text);
 681  E :    logger_->set_minidump_on_failure(flags_.minidump_on_failure);
 682  E :  }
 683    :  
 684  E :  void AsanRuntime::set_flags(const AsanFlags* flags) {
 685  E :    DCHECK(flags != NULL);
 686  E :    flags_ = *flags;
 687  E :  }
 688    :  
 689  E :  void AsanRuntime::AddHeap(HeapProxy* heap) {
 690  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 691  E :    InsertTailList(&heap_proxy_dlist_, HeapProxy::ToListEntry(heap));
 692  E :  }
 693    :  
 694  E :  void AsanRuntime::RemoveHeap(HeapProxy* heap) {
 695  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 696    :    DCHECK(HeapListContainsEntry(&heap_proxy_dlist_,
 697  E :                                 HeapProxy::ToListEntry(heap)));
 698  E :    RemoveEntryList(HeapProxy::ToListEntry(heap));
 699  E :  }
 700    :  
 701  E :  void AsanRuntime::GetBadAccessInformation(AsanErrorInfo* error_info) {
 702  E :    base::AutoLock lock(heap_proxy_dlist_lock_);
 703    :  
 704    :    // Checks if this is an access to an internal structure or if it's an access
 705    :    // in the upper region of the memory (over the 2 GB limit).
 706    :    if ((reinterpret_cast<size_t>(error_info->location) & (1 << 31)) != 0 ||
 707    :        Shadow::GetShadowMarkerForAddress(error_info->location)
 708  E :            == Shadow::kAsanMemoryByte) {
 709  E :        error_info->error_type = HeapProxy::WILD_ACCESS;
 710  E :    } else if (Shadow::GetShadowMarkerForAddress(error_info->location) ==
 711  E :        Shadow::kInvalidAddress) {
 712  E :      error_info->error_type = HeapProxy::INVALID_ADDRESS;
 713  E :    } else {
 714    :      // TODO(sebmarchand): Add some code to check if the heap is corrupted.
 715  E :      HeapProxy::GetBadAccessInformation(error_info);
 716    :    }
 717  E :  }
 718    :  
 719    :  }  // namespace asan
 720    :  }  // namespace agent

Coverage information generated Wed Dec 11 11:34:16 2013.