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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
93.5%3443680.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 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/error_info.h"
  16    :  
  17    :  #include <string>
  18    :  
  19    :  #include "base/strings/string_util.h"
  20    :  #include "syzygy/agent/asan/block_utils.h"
  21    :  #include "syzygy/agent/asan/runtime.h"
  22    :  #include "syzygy/agent/asan/shadow.h"
  23    :  #include "syzygy/agent/asan/stack_capture_cache.h"
  24    :  #include "syzygy/crashdata/crashdata.h"
  25    :  
  26    :  namespace agent {
  27    :  namespace asan {
  28    :  
  29    :  namespace {
  30    :  
  31    :  // Copy a stack capture object into an array.
  32    :  // @param stack_capture The stack capture that we want to copy.
  33    :  // @param dst Will receive the stack frames.
  34    :  // @param dst_size Will receive the number of frames that has been copied.
  35    :  void CopyStackCaptureToArray(const common::StackCapture* stack_capture,
  36    :                               void* dst,
  37  E :                               uint8_t* dst_size) {
  38  E :    DCHECK_NE(static_cast<common::StackCapture*>(nullptr), stack_capture);
  39  E :    DCHECK_NE(static_cast<void*>(nullptr), dst);
  40  E :    DCHECK_NE(static_cast<uint8_t*>(nullptr), dst_size);
  41  E :    ::memcpy(dst,
  42    :             stack_capture->frames(),
  43    :             stack_capture->num_frames() * sizeof(void*));
  44  E :    *dst_size = static_cast<uint8_t>(stack_capture->num_frames());
  45  E :  }
  46    :  
  47    :  // Get the information about an address relative to a block.
  48    :  // @param shadow The shadow memory to query.
  49    :  // @param header The header of the block containing this address.
  50    :  // @param bad_access_info Will receive the information about this address.
  51    :  void GetAddressInformation(const Shadow* shadow,
  52    :                             BlockHeader* header,
  53  E :                             AsanErrorInfo* bad_access_info) {
  54  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
  55  E :    DCHECK_NE(static_cast<BlockHeader*>(nullptr), header);
  56  E :    DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), bad_access_info);
  57  E :    DCHECK_NE(static_cast<void*>(nullptr), bad_access_info->location);
  58    :  
  59  E :    BlockInfo block_info = {};
  60  E :    shadow->BlockInfoFromShadow(header, &block_info);
  61  E :    SSIZE_T offset = 0;
  62  E :    char* offset_relativity = "";
  63  E :    switch (bad_access_info->error_type) {
  64    :      case HEAP_BUFFER_OVERFLOW: {
  65  E :        offset = static_cast<const uint8_t*>(bad_access_info->location) -
  66    :                 block_info.RawBody() - block_info.body_size;
  67  E :        offset_relativity = "beyond";
  68  E :        break;
  69    :      }
  70    :      case HEAP_BUFFER_UNDERFLOW: {
  71  E :        offset = block_info.RawBody() -
  72    :                 static_cast<const uint8_t*>(bad_access_info->location);
  73  E :        offset_relativity = "before";
  74  E :        break;
  75    :      }
  76    :      case USE_AFTER_FREE: {
  77  E :        offset = static_cast<const uint8_t*>(bad_access_info->location) -
  78    :                 block_info.RawBody();
  79  E :        offset_relativity = "inside";
  80  E :        break;
  81    :      }
  82    :      case WILD_ACCESS:
  83    :      case DOUBLE_FREE:
  84    :      case UNKNOWN_BAD_ACCESS:
  85    :      case CORRUPT_BLOCK:
  86  E :        return;
  87    :      default:
  88  i :        NOTREACHED() << "Error trying to dump address information.";
  89    :    }
  90    :  
  91  E :    size_t shadow_info_bytes = base::snprintf(
  92    :        bad_access_info->shadow_info,
  93    :        arraysize(bad_access_info->shadow_info) - 1,
  94    :        "%08X is %d bytes %s %d-byte block [%08X,%08X)\n",
  95    :        bad_access_info->location,
  96    :        offset,
  97    :        offset_relativity,
  98    :        block_info.body_size,
  99    :        block_info.body,
 100    :        block_info.trailer_padding);
 101    :  
 102  E :    std::string shadow_memory;
 103  E :    shadow->AppendShadowArrayText(
 104    :        bad_access_info->location, &shadow_memory);
 105  E :    size_t shadow_mem_bytes = base::snprintf(
 106    :        bad_access_info->shadow_memory,
 107    :        arraysize(bad_access_info->shadow_memory) - 1,
 108    :        "%s",
 109    :        shadow_memory.c_str());
 110    :  
 111    :    // Ensure that we had enough space to store the full shadow information.
 112  E :    DCHECK_LE(shadow_info_bytes, arraysize(bad_access_info->shadow_info) - 1);
 113  E :    DCHECK_LE(shadow_mem_bytes, arraysize(bad_access_info->shadow_memory) - 1);
 114  E :  }
 115    :  
 116    :  }  // namespace
 117    :  
 118    :  const char kHeapUseAfterFree[] = "heap-use-after-free";
 119    :  const char kHeapBufferUnderFlow[] = "heap-buffer-underflow";
 120    :  const char kHeapBufferOverFlow[] = "heap-buffer-overflow";
 121    :  const char kAttemptingDoubleFree[] = "attempting double-free";
 122    :  const char kInvalidAddress[] = "invalid-address";
 123    :  const char kWildAccess[] = "wild-access";
 124    :  const char kHeapUnknownError[] = "heap-unknown-error";
 125    :  const char kHeapCorruptBlock[] = "corrupt-block";
 126    :  const char kCorruptHeap[] = "corrupt-heap";
 127    :  
 128  E :  const char* ErrorInfoAccessTypeToStr(BadAccessKind bad_access_kind) {
 129  E :    switch (bad_access_kind) {
 130    :      case USE_AFTER_FREE:
 131  E :        return kHeapUseAfterFree;
 132    :      case HEAP_BUFFER_UNDERFLOW:
 133  E :        return kHeapBufferUnderFlow;
 134    :      case HEAP_BUFFER_OVERFLOW:
 135  E :        return kHeapBufferOverFlow;
 136    :      case WILD_ACCESS:
 137  E :        return kWildAccess;
 138    :      case INVALID_ADDRESS:
 139  E :        return kInvalidAddress;
 140    :      case DOUBLE_FREE:
 141  E :        return kAttemptingDoubleFree;
 142    :      case UNKNOWN_BAD_ACCESS:
 143  E :        return kHeapUnknownError;
 144    :      case CORRUPT_BLOCK:
 145  E :        return kHeapCorruptBlock;
 146    :      case CORRUPT_HEAP:
 147  E :        return kCorruptHeap;
 148    :      default:
 149  i :        NOTREACHED() << "Unexpected bad access kind.";
 150  i :        return nullptr;
 151    :    }
 152  E :  }
 153    :  
 154    :  namespace {
 155    :  
 156    :  // Converts an access kind string to its corresponding enum value.
 157    :  BadAccessKind ErrorInfoAccessTypeStrToEnum(const std::string& access_kind) {
 158    :    if (::strcmp(kHeapUseAfterFree, access_kind.c_str())) {
 159    :      return USE_AFTER_FREE;
 160    :    } else if (::strcmp(kHeapBufferUnderFlow, access_kind.c_str()) == 0) {
 161    :      return HEAP_BUFFER_UNDERFLOW;
 162    :    } else if (::strcmp(kHeapBufferOverFlow, access_kind.c_str()) == 0) {
 163    :      return HEAP_BUFFER_OVERFLOW;
 164    :    } else if (::strcmp(kWildAccess, access_kind.c_str()) == 0) {
 165    :      return WILD_ACCESS;
 166    :    } else if (::strcmp(kInvalidAddress, access_kind.c_str()) == 0) {
 167    :      return INVALID_ADDRESS;
 168    :    } else if (::strcmp(kAttemptingDoubleFree, access_kind.c_str()) == 0) {
 169    :      return DOUBLE_FREE;
 170    :    } else if (::strcmp(kHeapUnknownError, access_kind.c_str()) == 0) {
 171    :      return UNKNOWN_BAD_ACCESS;
 172    :    } else if (::strcmp(kHeapCorruptBlock, access_kind.c_str()) == 0) {
 173    :      return CORRUPT_BLOCK;
 174    :    } else if (::strcmp(kCorruptHeap, access_kind.c_str()) == 0) {
 175    :      return CORRUPT_HEAP;
 176    :    } else {
 177    :      NOTREACHED() << "Unexpected bad access kind.";
 178    :      return BAD_ACCESS_KIND_MAX;
 179    :    }
 180    :  }
 181    :  
 182    :  }  // namespace
 183    :  
 184    :  bool ErrorInfoGetBadAccessInformation(const Shadow* shadow,
 185    :                                        StackCaptureCache* stack_cache,
 186  E :                                        AsanErrorInfo* bad_access_info) {
 187  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 188  E :    DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache);
 189  E :    DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), bad_access_info);
 190  E :    BlockInfo block_info = {};
 191  E :    if (!shadow->BlockInfoFromShadow(
 192    :        bad_access_info->location, &block_info)) {
 193  E :      return false;
 194    :    }
 195    :  
 196    :    // Fill out the information about the primary block.
 197  E :    ErrorInfoGetAsanBlockInfo(shadow, block_info, stack_cache,
 198    :                              &bad_access_info->block_info);
 199    :  
 200  E :    if (bad_access_info->error_type != DOUBLE_FREE &&
 201    :        bad_access_info->error_type != CORRUPT_BLOCK) {
 202  E :      bad_access_info->error_type = ErrorInfoGetBadAccessKind(
 203    :          shadow, bad_access_info->location, block_info.header);
 204    :    }
 205    :  
 206    :    // Get the bad access description if we've been able to determine its kind.
 207  E :    if (bad_access_info->error_type != UNKNOWN_BAD_ACCESS) {
 208  E :      GetAddressInformation(shadow, block_info.header, bad_access_info);
 209  E :      return true;
 210    :    }
 211    :  
 212  i :    return false;
 213  E :  }
 214    :  
 215    :  BadAccessKind ErrorInfoGetBadAccessKind(const Shadow* shadow,
 216    :                                          const void* addr,
 217  E :                                          const BlockHeader* header) {
 218  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 219  E :    DCHECK_NE(static_cast<const void*>(nullptr), addr);
 220  E :    DCHECK_NE(static_cast<const BlockHeader*>(nullptr), header);
 221    :  
 222  E :    switch (static_cast<BlockState>(header->state)) {
 223    :      case ALLOCATED_BLOCK: {
 224  E :        BlockInfo block_info = {};
 225  E :        shadow->BlockInfoFromShadow(header, &block_info);
 226  E :        if (addr < block_info.body) {
 227  E :          return HEAP_BUFFER_UNDERFLOW;
 228  E :        } else if (addr >= (block_info.RawBody() + block_info.body_size)) {
 229  E :          return HEAP_BUFFER_OVERFLOW;
 230  i :        } else if (shadow->GetShadowMarkerForAddress(addr) ==
 231    :            kHeapFreedMarker) {
 232    :          // This is a use after free on a block managed by a nested heap.
 233  i :          return USE_AFTER_FREE;
 234    :        }
 235  i :        break;
 236    :      }
 237    :  
 238    :      case QUARANTINED_BLOCK:
 239    :      case QUARANTINED_FLOODED_BLOCK:
 240    :      case FREED_BLOCK: {
 241  E :        return USE_AFTER_FREE;
 242    :        break;
 243    :      }
 244    :    }
 245    :  
 246  i :    return UNKNOWN_BAD_ACCESS;
 247  E :  }
 248    :  
 249    :  void ErrorInfoGetAsanBlockInfo(const Shadow* shadow,
 250    :                                 const BlockInfo& block_info,
 251    :                                 StackCaptureCache* stack_cache,
 252  E :                                 AsanBlockInfo* asan_block_info) {
 253  E :    DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache);
 254  E :    DCHECK_NE(static_cast<AsanBlockInfo*>(nullptr), asan_block_info);
 255    :  
 256  E :    ::memset(asan_block_info, 0, sizeof(*asan_block_info));
 257  E :    BlockState block_state = BlockDetermineMostLikelyState(shadow, block_info);
 258  E :    BlockAnalyze(block_state, block_info, &asan_block_info->analysis);
 259    :  
 260  E :    asan_block_info->header = block_info.header;
 261  E :    asan_block_info->user_size = block_info.header->body_size;
 262  E :    asan_block_info->state = block_info.header->state;
 263  E :    asan_block_info->alloc_tid = block_info.trailer->alloc_tid;
 264  E :    asan_block_info->free_tid = block_info.trailer->free_tid;
 265    :  
 266  E :    if (block_info.header->state != ALLOCATED_BLOCK &&
 267    :        block_info.trailer->free_ticks != 0) {
 268  E :      asan_block_info->milliseconds_since_free =
 269    :          ::GetTickCount() - block_info.trailer->free_ticks;
 270    :    }
 271    :  
 272    :    // TODO(chrisha): Use detailed analysis results to do this more efficiently.
 273  E :    asan_block_info->heap_type = kUnknownHeapType;
 274  E :    HeapManagerInterface::HeapId heap_id = block_info.trailer->heap_id;
 275  E :    if (heap_id != 0) {
 276  E :      AsanRuntime* runtime = AsanRuntime::runtime();
 277  E :      DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
 278  E :      asan_block_info->heap_type = runtime->GetHeapType(heap_id);
 279    :    }
 280    :  
 281    :    // Copy the alloc and free stack traces if they're valid.
 282    :    // TODO(chrisha): Use detailed analysis results that have been gathered
 283    :    //                once, rather than recalculating this.
 284  E :    if (stack_cache->StackCapturePointerIsValid(
 285    :            block_info.header->alloc_stack)) {
 286  E :      CopyStackCaptureToArray(block_info.header->alloc_stack,
 287    :                              asan_block_info->alloc_stack,
 288    :                              &asan_block_info->alloc_stack_size);
 289    :    }
 290  E :    if (block_info.header->state != ALLOCATED_BLOCK &&
 291    :        stack_cache->StackCapturePointerIsValid(
 292    :            block_info.header->free_stack)) {
 293  E :      CopyStackCaptureToArray(block_info.header->free_stack,
 294    :                              asan_block_info->free_stack,
 295    :                              &asan_block_info->free_stack_size);
 296    :    }
 297  E :  }
 298    :  
 299    :  void GetAsanErrorShadowMemory(const Shadow* shadow,
 300    :                                const void* error_location,
 301  E :                                AsanErrorShadowMemory* shadow_memory) {
 302  E :    DCHECK_NE(static_cast<AsanErrorShadowMemory*>(nullptr), shadow_memory);
 303    :  
 304  E :    shadow_memory->index = reinterpret_cast<uintptr_t>(error_location);
 305  E :    shadow_memory->index >>= kShadowRatioLog;
 306  E :    shadow_memory->index = (shadow_memory->index / shadow->kShadowBytesPerLine) *
 307    :                           Shadow::kShadowBytesPerLine;
 308    :  
 309    :    uintptr_t index_min =
 310  E :        shadow_memory->index -
 311    :        Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
 312  E :    if (index_min > shadow_memory->index)
 313  i :      index_min = 0;
 314    :    uintptr_t index_max =
 315  E :        shadow_memory->index +
 316    :        Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
 317  E :    if (index_max < shadow_memory->index)
 318  i :      index_max = 0;
 319    :  
 320  E :    shadow_memory->address =
 321    :        reinterpret_cast<uintptr_t>(shadow->shadow() + index_min);
 322  E :    shadow_memory->length = index_max - index_min;
 323  E :  }
 324    :  
 325    :  namespace {
 326    :  
 327    :  const char kAllocatedBlock[] = "allocated";
 328    :  const char kQuarantinedBlock[] = "quarantined";
 329    :  const char kQuarantinedFloodedBlock[] = "quarantined (flooded)";
 330    :  const char kFreedBlock[] = "freed";
 331    :  const char kAccessModeRead[] = "read";
 332    :  const char kAccessModeWrite[] = "write";
 333    :  const char kAccessModeUnknown[] = "(unknown)";
 334    :  
 335    :  // Converts an access mode to a string.
 336  E :  void AccessModeToString(AccessMode access_mode, std::string* str) {
 337  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 338  E :    switch (access_mode) {
 339    :      case ASAN_READ_ACCESS:
 340  E :        *str = kAccessModeRead;
 341  E :        break;
 342    :      case ASAN_WRITE_ACCESS:
 343  E :        *str = kAccessModeWrite;
 344  E :        break;
 345    :      default:
 346  E :        *str = kAccessModeUnknown;
 347    :        break;
 348    :    }
 349  E :  }
 350    :  
 351    :  // Converts an access mode string to its corresponding enum value.
 352    :  AccessMode AccessModeStringToEnum(const std::string& access_mode) {
 353    :    if (::strcmp(kAccessModeRead, access_mode.c_str()) == 0) {
 354    :      return ASAN_READ_ACCESS;
 355    :    } else if (::strcmp(kAccessModeWrite, access_mode.c_str()) == 0) {
 356    :      return ASAN_WRITE_ACCESS;
 357    :    } else if (::strcmp(kAccessModeUnknown, access_mode.c_str()) == 0) {
 358    :      return ASAN_UNKNOWN_ACCESS;
 359    :    } else {
 360    :      NOTREACHED() << "Unexpected access mode.";
 361    :      return ASAN_ACCESS_MODE_MAX;
 362    :    }
 363    :  }
 364    :  
 365    :  // Converts a block state to a string.
 366  E :  void BlockStateToString(BlockState block_state, std::string* str) {
 367  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 368  E :    switch (block_state) {
 369    :      case ALLOCATED_BLOCK:
 370  E :        *str = kAllocatedBlock;
 371  E :        break;
 372    :      case QUARANTINED_BLOCK:
 373  E :        *str = kQuarantinedBlock;
 374  E :        break;
 375    :      case QUARANTINED_FLOODED_BLOCK:
 376  E :        *str = kQuarantinedFloodedBlock;
 377  E :        break;
 378  i :      case FREED_BLOCK: *str = "freed"; break;
 379    :    }
 380  E :  }
 381    :  
 382    :  // Converts a block state string to its corresponding enum value.
 383  E :  BlockState BlockStateStringToEnum(const std::string& block_state) {
 384  E :    if (::strcmp(kAllocatedBlock, block_state.c_str()) == 0) {
 385  i :      return ALLOCATED_BLOCK;
 386  E :    } else if (::strcmp(kQuarantinedBlock, block_state.c_str()) == 0) {
 387  i :      return QUARANTINED_BLOCK;
 388  E :    } else if (::strcmp(kQuarantinedFloodedBlock, block_state.c_str()) == 0) {
 389  E :      return QUARANTINED_FLOODED_BLOCK;
 390  i :    } else if (::strcmp(kFreedBlock, block_state.c_str()) == 0) {
 391  i :      return FREED_BLOCK;
 392  i :    } else {
 393  i :      NOTREACHED() << "Unexpected block state.";
 394  i :      return BLOCK_STATE_MAX;
 395    :    }
 396  E :  }
 397    :  
 398    :  // Converts a heap type string to its corresponding enum value.
 399  E :  HeapType HeapTypeStrToEnum(const std::string& heap_type) {
 400  E :    for (size_t i = 0; i < kHeapTypeMax; ++i) {
 401  E :      if (::strcmp(kHeapTypes[i], heap_type.c_str()) == 0) {
 402  E :        return static_cast<HeapType>(i);
 403    :      }
 404  E :    }
 405  i :    NOTREACHED() << "Unexpected heap type.";
 406  i :    return kHeapTypeMax;
 407  E :  }
 408    :  
 409  E :  uint64_t CastAddress(const void* address) {
 410  E :    return static_cast<uint64_t>(reinterpret_cast<uint32_t>(address));
 411  E :  }
 412    :  
 413    :  void PopulateStackTrace(const void* const* frames,
 414    :                          size_t frame_count,
 415  E :                          crashdata::StackTrace* stack_trace) {
 416  E :    DCHECK_NE(static_cast<void*>(nullptr), frames);
 417  E :    DCHECK_LT(0u, frame_count);
 418  E :    DCHECK_NE(static_cast<crashdata::StackTrace*>(nullptr), stack_trace);
 419  E :    for (size_t i = 0; i < frame_count; ++i)
 420  E :      stack_trace->add_frames(CastAddress(frames[i]));
 421  E :  }
 422    :  
 423  E :  void DataStateToString(DataState data_state, std::string* str) {
 424  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 425  E :    switch (data_state) {
 426    :      default:
 427  E :      case kDataStateUnknown: *str = "(unknown)"; break;
 428  E :      case kDataIsClean: *str = "clean"; break;
 429  E :      case kDataIsCorrupt: *str = "corrupt"; break;
 430    :    }
 431  E :  }
 432    :  
 433    :  void PopulateBlockAnalysisResult(const BlockAnalysisResult& analysis,
 434  E :                                   crashdata::Dictionary* dict) {
 435  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 436  E :    DataStateToString(
 437    :        analysis.block_state,
 438    :        crashdata::LeafGetString(crashdata::DictAddLeaf("block", dict)));
 439  E :    DataStateToString(
 440    :        analysis.header_state,
 441    :        crashdata::LeafGetString(crashdata::DictAddLeaf("header", dict)));
 442  E :    DataStateToString(
 443    :        analysis.body_state,
 444    :        crashdata::LeafGetString(crashdata::DictAddLeaf("body", dict)));
 445  E :    DataStateToString(
 446    :        analysis.trailer_state,
 447    :        crashdata::LeafGetString(crashdata::DictAddLeaf("trailer", dict)));
 448  E :  }
 449    :  
 450    :  }  // namespace
 451    :  
 452    :  void PopulateBlockInfo(const Shadow* shadow,
 453    :                         const AsanBlockInfo& block_info,
 454    :                         bool include_block_contents,
 455    :                         crashdata::Value* value,
 456  E :                         MemoryRanges* memory_ranges) {
 457  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 458  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 459    :  
 460  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 461  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 462    :  
 463    :    // Set block properties.
 464    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("header", dict))
 465  E :        ->set_address(CastAddress(block_info.header));
 466  E :    crashdata::LeafSetUInt(block_info.user_size,
 467    :                           crashdata::DictAddLeaf("user-size", dict));
 468  E :    BlockStateToString(
 469    :        static_cast<BlockState>(block_info.state),
 470    :        crashdata::LeafGetString(crashdata::DictAddLeaf("state", dict)));
 471    :    crashdata::LeafGetString(crashdata::DictAddLeaf("heap-type", dict))
 472  E :        ->assign(kHeapTypes[block_info.heap_type]);
 473    :  
 474    :    // Set the block analysis.
 475  E :    PopulateBlockAnalysisResult(
 476    :        block_info.analysis,
 477    :        crashdata::ValueGetDict(crashdata::DictAddValue("analysis", dict)));
 478    :  
 479    :    // Set the allocation information.
 480  E :    if (block_info.alloc_stack_size != 0) {
 481  E :      crashdata::LeafSetUInt(block_info.alloc_tid,
 482    :                             crashdata::DictAddLeaf("alloc-thread-id", dict));
 483  E :      PopulateStackTrace(block_info.alloc_stack,
 484    :                         block_info.alloc_stack_size,
 485    :                         crashdata::LeafGetStackTrace(
 486    :                             crashdata::DictAddLeaf("alloc-stack", dict)));
 487    :    }
 488    :  
 489    :    // Set the free information if available.
 490  E :    if (block_info.free_stack_size != 0) {
 491  E :      crashdata::LeafSetUInt(block_info.free_tid,
 492    :                             crashdata::DictAddLeaf("free-thread-id", dict));
 493  E :      PopulateStackTrace(block_info.free_stack,
 494    :                         block_info.free_stack_size,
 495    :                         crashdata::LeafGetStackTrace(
 496    :                             crashdata::DictAddLeaf("free-stack", dict)));
 497  E :      crashdata::LeafSetUInt(
 498    :          block_info.milliseconds_since_free,
 499    :          crashdata::DictAddLeaf("milliseconds-since-free", dict));
 500    :    }
 501    :  
 502  E :    if (include_block_contents) {
 503    :      // Get the full block information from the shadow memory.
 504  E :      BlockInfo full_block_info = {};
 505  E :      shadow->BlockInfoFromShadow(block_info.header, &full_block_info);
 506    :  
 507    :      // Copy the entire block contents.
 508  E :      crashdata::Blob* blob = crashdata::LeafGetBlob(
 509    :          crashdata::DictAddLeaf("contents", dict));
 510  E :      blob->mutable_address()->set_address(CastAddress(block_info.header));
 511    :  
 512    :      // Use memory range feature if available. Fallback on blob data.
 513  E :      if (memory_ranges) {
 514    :        // protobuf accepts uint32 as size, so it must fit.
 515  E :        DCHECK_LE(full_block_info.block_size,
 516  E :                  static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
 517  E :        blob->set_size(static_cast<uint32_t>(full_block_info.block_size));
 518    :  
 519  E :        memory_ranges->push_back(std::pair<const char*, size_t>(
 520    :            static_cast<const char*>(block_info.header),
 521    :            full_block_info.block_size));
 522  E :      } else {
 523  E :        blob->mutable_data()->assign(
 524    :            reinterpret_cast<const char*>(block_info.header),
 525    :            full_block_info.block_size);
 526    :      }
 527    :  
 528    :      // Copy the associated shadow memory.
 529    :      size_t shadow_index =
 530  E :          reinterpret_cast<size_t>(block_info.header) / kShadowRatio;
 531  E :      size_t shadow_length = full_block_info.block_size / kShadowRatio;
 532    :      const char* shadow_data =
 533  E :          reinterpret_cast<const char*>(shadow->shadow()) +
 534    :          shadow_index;
 535  E :      blob = crashdata::LeafGetBlob(
 536    :          crashdata::DictAddLeaf("shadow", dict));
 537  E :      blob->mutable_address()->set_address(CastAddress(shadow_data));
 538    :  
 539    :      // Use memory range feature if available. Fallback on blob data.
 540  E :      if (memory_ranges) {
 541    :        // protobuf accepts uint32 as size, so it must fit.
 542  E :        DCHECK_LE(shadow_length,
 543  E :                  static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
 544  E :        blob->set_size(static_cast<uint32_t>(shadow_length));
 545    :  
 546  E :        memory_ranges->push_back(
 547    :            std::pair<const char*, size_t>(shadow_data, shadow_length));
 548  E :      } else {
 549  E :        blob->mutable_data()->assign(shadow_data, shadow_length);
 550    :      }
 551    :    }
 552  E :  }
 553    :  
 554    :  void PopulateCorruptBlockRange(const Shadow* shadow,
 555    :                                 const AsanCorruptBlockRange& range,
 556    :                                 crashdata::Value* value,
 557  E :                                 MemoryRanges* memory_ranges) {
 558  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 559  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 560    :  
 561  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 562  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 563    :  
 564    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("address", dict))
 565  E :        ->set_address(CastAddress(range.address));
 566  E :    crashdata::LeafSetUInt(range.length, crashdata::DictAddLeaf("length", dict));
 567  E :    crashdata::LeafSetUInt(range.block_count,
 568    :                           crashdata::DictAddLeaf("block-count", dict));
 569    :  
 570    :    // Add the blocks.
 571  E :    if (range.block_info_count > 0) {
 572    :      crashdata::ValueList* list =
 573  E :          crashdata::ValueGetValueList(crashdata::DictAddValue("blocks", dict));
 574  E :      for (size_t i = 0; i < range.block_info_count; ++i) {
 575  E :        if (range.block_info[i].header != nullptr)
 576    :          // Emit the block info but don't explicitly include the contents.
 577  E :          PopulateBlockInfo(shadow, range.block_info[i], false,
 578    :                            list->add_values(), memory_ranges);
 579  E :      }
 580    :    }
 581  E :  }
 582    :  
 583    :  namespace {
 584    :  
 585    :  void PopulateShadowMemoryBlob(const Shadow* shadow,
 586    :                                const AsanErrorInfo& error_info,
 587    :                                crashdata::Dictionary* dict,
 588  E :                                MemoryRanges* memory_ranges) {
 589  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 590  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 591    :  
 592    :    // The shadow-info string can be reconstructed from information already in
 593    :    // the crash (location, block-info, access-mode, access-size), so there's no
 594    :    // need to send it. This is emitted as a blob.
 595  E :    AsanErrorShadowMemory shadow_memory = {};
 596  E :    GetAsanErrorShadowMemory(shadow, error_info.location, &shadow_memory);
 597    :  
 598  E :    crashdata::LeafSetUInt(shadow_memory.index,
 599    :                           crashdata::DictAddLeaf("shadow-memory-index", dict));
 600  E :    crashdata::Blob* blob = crashdata::LeafGetBlob(
 601    :        crashdata::DictAddLeaf("shadow-memory", dict));
 602  E :    blob->mutable_address()->set_address(shadow_memory.address);
 603    :  
 604    :    // Use memory range feature if available. Fallback on blob data.
 605  E :    if (memory_ranges) {
 606  E :      blob->set_size(shadow_memory.length);
 607    :  
 608  E :      const char* data = reinterpret_cast<const char*>(shadow_memory.address);
 609  E :      memory_ranges->push_back(
 610    :          std::pair<const char*, size_t>(data, shadow_memory.length));
 611  E :    } else {
 612  E :      blob->mutable_data()->assign(
 613    :          reinterpret_cast<const char*>(shadow_memory.address),
 614    :          shadow_memory.length);
 615    :    }
 616  E :  }
 617    :  
 618    :  void PopulatePageBitsBlob(const Shadow* shadow,
 619    :                            const AsanErrorInfo& error_info,
 620    :                            crashdata::Dictionary* dict,
 621  E :                            MemoryRanges* memory_ranges) {
 622  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 623    :  
 624    :    // Emit information about page protections surround the address in question.
 625    :    static const size_t kPageBitsContext = 2;
 626  E :    uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
 627  E :    index /= GetPageSize();  // 1 bit per page.
 628  E :    index /= 8;  // 8 bits per byte.
 629  E :    uintptr_t index_min = index - kPageBitsContext;
 630  E :    if (index_min > index)
 631  E :      index_min = 0;
 632  E :    uintptr_t index_max = index + 1 + kPageBitsContext;
 633  E :    if (index_max < index)
 634  i :      index_max = 0;
 635  E :    uintptr_t length = index_max - index_min;
 636    :  
 637  E :    crashdata::LeafSetUInt(
 638    :        index, crashdata::DictAddLeaf("page-bits-index", dict));
 639    :    crashdata::Blob* blob =
 640  E :        crashdata::LeafGetBlob(crashdata::DictAddLeaf("page-bits", dict));
 641  E :    blob->mutable_address()->set_address(
 642    :        CastAddress(shadow->page_bits() + index_min));
 643    :  
 644    :    // Use memory range feature if available. Fallback on blob data.
 645  E :    if (memory_ranges) {
 646  E :      blob->set_size(length);
 647    :  
 648    :      const char* data =
 649  E :          reinterpret_cast<const char*>(shadow->page_bits() + index_min);
 650  E :      memory_ranges->push_back(std::pair<const char*, size_t>(data, length));
 651  E :    } else {
 652  E :      blob->mutable_data()->assign(
 653    :          reinterpret_cast<const char*>(shadow->page_bits() + index_min), length);
 654    :    }
 655  E :  }
 656    :  
 657    :  void PopulateAsanParameters(const AsanErrorInfo& error_info,
 658  E :                              crashdata::Dictionary* dict) {
 659  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 660    :  
 661    :    // Any new parameter added to the parameters structure should also be added
 662    :    // here.
 663    :    static_assert(14 == ::common::kAsanParametersVersion,
 664    :                  "Pointers in the params must be linked up here.");
 665  E :    crashdata::Dictionary* param_dict = crashdata::DictAddDict("asan-parameters",
 666    :                                                               dict);
 667  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), param_dict);
 668  E :    crashdata::LeafSetUInt(error_info.asan_parameters.quarantine_size,
 669    :                           crashdata::DictAddLeaf("quarantine-size", param_dict));
 670  E :    crashdata::LeafSetUInt(error_info.asan_parameters.trailer_padding_size,
 671    :                           crashdata::DictAddLeaf("trailer-padding-size",
 672    :                                                  param_dict));
 673  E :    crashdata::LeafSetUInt(error_info.asan_parameters.quarantine_block_size,
 674    :                           crashdata::DictAddLeaf("quarantine-block-size",
 675    :                                                  param_dict));
 676  E :    crashdata::LeafSetUInt(error_info.asan_parameters.check_heap_on_failure,
 677    :                           crashdata::DictAddLeaf("check-heap-on-failure",
 678    :                                                  param_dict));
 679  E :    crashdata::LeafSetUInt(error_info.asan_parameters.enable_zebra_block_heap,
 680    :                           crashdata::DictAddLeaf("enable-zebra-block-heap",
 681    :                                                  param_dict));
 682  E :    crashdata::LeafSetUInt(error_info.asan_parameters.enable_large_block_heap,
 683    :                           crashdata::DictAddLeaf("enable-large-block-heap",
 684    :                                                  param_dict));
 685  E :    crashdata::LeafSetUInt(error_info.asan_parameters.enable_allocation_filter,
 686    :                           crashdata::DictAddLeaf("enable-allocation-filter",
 687    :                                                  param_dict));
 688  E :    crashdata::LeafSetReal(error_info.asan_parameters.allocation_guard_rate,
 689    :                           crashdata::DictAddLeaf("allocation-guard-rate",
 690    :                                                  param_dict));
 691  E :    crashdata::LeafSetUInt(error_info.asan_parameters.zebra_block_heap_size,
 692    :                           crashdata::DictAddLeaf("zebra-block-heap-size",
 693    :                                                  param_dict));
 694  E :    crashdata::LeafSetReal(
 695    :        error_info.asan_parameters.zebra_block_heap_quarantine_ratio,
 696    :        crashdata::DictAddLeaf("zebra-block-heap-quarantine-ratio", param_dict));
 697  E :    crashdata::LeafSetUInt(error_info.asan_parameters.large_allocation_threshold,
 698    :                           crashdata::DictAddLeaf("large-allocation-threshold",
 699    :                                                  param_dict));
 700  E :    crashdata::LeafSetReal(
 701    :        error_info.asan_parameters.quarantine_flood_fill_rate,
 702    :        crashdata::DictAddLeaf("quarantine-flood-fill-rate", param_dict));
 703  E :  }
 704    :  
 705    :  }  // namespace
 706    :  
 707    :  // TODO(chrisha): Only emit information that makes sense for the given error
 708    :  //                type. For example, wild-access errors have no associated
 709    :  //                block information.
 710    :  void PopulateErrorInfo(const Shadow* shadow,
 711    :                         const AsanErrorInfo& error_info,
 712    :                         crashdata::Value* value,
 713  E :                         MemoryRanges* memory_ranges) {
 714  E :    DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
 715  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 716    :  
 717    :    // Create a single outermost dictionary.
 718  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 719  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 720    :  
 721    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("location", dict))
 722  E :        ->set_address(CastAddress(error_info.location));
 723  E :    crashdata::LeafSetUInt(error_info.crash_stack_id,
 724    :                           crashdata::DictAddLeaf("crash-stack-id", dict));
 725  E :    if (error_info.block_info.header != nullptr) {
 726    :      // Include the block contents only if the block isn't too large. This tries
 727    :      // to reflect the cap on crash server minidump sizes.
 728    :      // TODO(chrisha): This decision should be made higher up the stack, and not
 729    :      // here.
 730  E :      bool include_block_info = error_info.block_info.user_size < 100 * 1024;
 731  E :      PopulateBlockInfo(shadow, error_info.block_info, include_block_info,
 732    :                        crashdata::DictAddValue("block-info", dict),
 733    :                        memory_ranges);
 734    :    }
 735    :    crashdata::LeafGetString(crashdata::DictAddLeaf("error-type", dict))
 736  E :        ->assign(ErrorInfoAccessTypeToStr(error_info.error_type));
 737  E :    AccessModeToString(
 738    :        error_info.access_mode,
 739    :        crashdata::LeafGetString(crashdata::DictAddLeaf("access-mode", dict)));
 740  E :    crashdata::LeafSetUInt(error_info.access_size,
 741    :                           crashdata::DictAddLeaf("access-size", dict));
 742    :  
 743  E :    PopulateShadowMemoryBlob(shadow, error_info, dict, memory_ranges);
 744  E :    PopulatePageBitsBlob(shadow, error_info, dict, memory_ranges);
 745    :  
 746    :    // Send information about corruption.
 747  E :    crashdata::LeafSetUInt(error_info.heap_is_corrupt,
 748    :                           crashdata::DictAddLeaf("heap-is-corrupt", dict));
 749  E :    crashdata::LeafSetUInt(error_info.corrupt_range_count,
 750    :                           crashdata::DictAddLeaf("corrupt-range-count", dict));
 751  E :    crashdata::LeafSetUInt(error_info.corrupt_block_count,
 752    :                           crashdata::DictAddLeaf("corrupt-block-count", dict));
 753  E :    if (error_info.corrupt_ranges_reported > 0) {
 754  E :      crashdata::ValueList* list = crashdata::ValueGetValueList(
 755    :          crashdata::DictAddValue("corrupt-ranges", dict));
 756  E :      for (size_t i = 0; i < error_info.corrupt_ranges_reported; ++i) {
 757  E :        PopulateCorruptBlockRange(shadow, error_info.corrupt_ranges[i],
 758    :                                  list->add_values(), memory_ranges);
 759  E :      }
 760    :    }
 761  E :    PopulateAsanParameters(error_info, dict);
 762  E :  }
 763    :  
 764    :  void CrashdataProtobufToErrorInfo(const crashdata::Value& protobuf,
 765  E :                                    AsanErrorInfo* error_info) {
 766  E :    DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), error_info);
 767    :  
 768  E :    DCHECK_EQ(crashdata::Value_Type_DICTIONARY, protobuf.type());
 769    :  
 770  E :    const crashdata::Dictionary outer_dict = protobuf.dictionary();
 771  E :    DCHECK(outer_dict.IsInitialized());
 772  E :    for (const auto& iter : outer_dict.values()) {
 773  E :      if (iter.value().has_leaf()) {
 774  E :        const crashdata::Leaf& leaf = iter.value().leaf();
 775  E :        if (base::LowerCaseEqualsASCII(iter.key(), "header")) {
 776  E :          DCHECK(leaf.has_address());
 777  E :          error_info->block_info.header =
 778    :              reinterpret_cast<const void*>(leaf.address().address());
 779  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "user-size")) {
 780  E :          DCHECK(leaf.has_unsigned_integer());
 781  E :          error_info->block_info.user_size = leaf.unsigned_integer();
 782  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "state")) {
 783  E :          DCHECK(leaf.has_string());
 784  E :          error_info->block_info.state = BlockStateStringToEnum(leaf.string());
 785  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "heap-type")) {
 786  E :          DCHECK(leaf.has_string());
 787  E :          error_info->block_info.heap_type = HeapTypeStrToEnum(leaf.string());
 788  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "alloc-thread-id")) {
 789  E :          DCHECK(leaf.has_unsigned_integer());
 790  E :          error_info->block_info.alloc_tid =
 791    :              static_cast<DWORD>(leaf.unsigned_integer());
 792  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "alloc-stack")) {
 793  E :          DCHECK(leaf.has_stack_trace());
 794    :          // TODO(sebmarchand): Parse this field.
 795  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "free-thread-id")) {
 796  E :          DCHECK(leaf.has_unsigned_integer());
 797  E :          error_info->block_info.free_tid =
 798    :              static_cast<DWORD>(leaf.unsigned_integer());
 799  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "free-stack")) {
 800  E :          DCHECK(leaf.has_stack_trace());
 801    :          // TODO(sebmarchand): Parse this field.
 802  E :        } else if (base::LowerCaseEqualsASCII(iter.key(),
 803    :                                              "milliseconds-since-free")) {
 804  E :          DCHECK(leaf.has_unsigned_integer());
 805  E :          error_info->block_info.milliseconds_since_free =
 806    :              leaf.unsigned_integer();
 807  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "contents")) {
 808  E :          DCHECK(leaf.has_blob());
 809    :          // TODO(sebmarchand): Parse this field.
 810  E :        } else if (base::LowerCaseEqualsASCII(iter.key(), "shadow")) {
 811  E :          DCHECK(leaf.has_blob());
 812    :          // TODO(sebmarchand): Parse this field.
 813  E :        } else {
 814  i :          NOTREACHED() << "Unexpected leaf entry.";
 815  E :        }
 816  E :      } else if (iter.value().has_dictionary()) {
 817  E :        if (base::LowerCaseEqualsASCII(iter.key(), "analysis")) {
 818    :          // TODO(sebmarchand): Parse this field.
 819  E :        } else {
 820  i :          NOTREACHED() << "Unexpected dictionary entry.";
 821    :        }
 822  E :      } else {
 823  i :        NOTREACHED() << "Unexpected entry.";
 824    :      }
 825  E :    }
 826  E :  }
 827    :  
 828    :  }  // namespace asan
 829    :  }  // namespace agent

Coverage information generated Fri Jul 29 11:00:21 2016.