Coverage for /Syzygy/kasko/minidump.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
92.1%931010.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/kasko/minidump.h"
  16    :  
  17    :  #include <Windows.h>  // NOLINT
  18    :  #include <DbgHelp.h>
  19    :  #include <Psapi.h>
  20    :  #include <winternl.h>
  21    :  
  22    :  #include "base/files/file.h"
  23    :  #include "base/process/process_handle.h"
  24    :  #include "base/win/pe_image.h"
  25    :  #include "base/win/scoped_handle.h"
  26    :  
  27    :  #include "syzygy/common/com_utils.h"
  28    :  #include "syzygy/core/address_range.h"
  29    :  #include "syzygy/kasko/loader_lock.h"
  30    :  
  31    :  namespace kasko {
  32    :  
  33    :  namespace {
  34    :  
  35    :  // Minidump with stacks, PEB, TEB, and unloaded module list.
  36    :  const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
  37    :      MiniDumpWithProcessThreadData |  // Get PEB and TEB.
  38    :      MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
  39    :  
  40    :  // Minidump with all of the above, plus memory referenced from stack.
  41    :  const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
  42    :      MiniDumpWithProcessThreadData |  // Get PEB and TEB.
  43    :      MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
  44    :      MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.
  45    :  
  46    :  // Large dump with all process memory.
  47    :  const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
  48    :      MiniDumpWithFullMemory |  // Full memory from process.
  49    :      MiniDumpWithProcessThreadData |  // Get PEB and TEB.
  50    :      MiniDumpWithHandleData |  // Get all handle information.
  51    :      MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
  52    :  
  53    :  class MinidumpCallbackHandler {
  54    :   public:
  55    :    explicit MinidumpCallbackHandler(
  56    :        const std::vector<MinidumpRequest::MemoryRange>* memory_ranges);
  57    :  
  58  E :    const MINIDUMP_CALLBACK_INFORMATION* GetMINIDUMP_CALLBACK_INFORMATION() {
  59  E :      return &minidump_callback_information_;
  60  E :    }
  61    :  
  62    :   private:
  63    :    BOOL MemoryCallback(ULONG64* memory_base, ULONG* memory_size);
  64    :  
  65    :    static BOOL CALLBACK
  66    :    CallbackRoutine(PVOID context,
  67    :                    const PMINIDUMP_CALLBACK_INPUT callback_input,
  68    :                    PMINIDUMP_CALLBACK_OUTPUT callback_output);
  69    :  
  70    :    const std::vector<MinidumpRequest::MemoryRange>* memory_ranges_;
  71    :    size_t next_memory_range_index_;
  72    :    MINIDUMP_CALLBACK_INFORMATION minidump_callback_information_;
  73    :  
  74    :    DISALLOW_COPY_AND_ASSIGN(MinidumpCallbackHandler);
  75    :  };
  76    :  
  77    :  MinidumpCallbackHandler::MinidumpCallbackHandler(
  78    :      const std::vector<MinidumpRequest::MemoryRange>* memory_ranges)
  79    :      : memory_ranges_(memory_ranges),
  80    :        next_memory_range_index_(0),
  81  E :        minidump_callback_information_() {
  82    :    minidump_callback_information_.CallbackRoutine =
  83  E :        &MinidumpCallbackHandler::CallbackRoutine;
  84  E :    minidump_callback_information_.CallbackParam = reinterpret_cast<void*>(this);
  85  E :  }
  86    :  
  87    :  BOOL MinidumpCallbackHandler::MemoryCallback(ULONG64* memory_base,
  88  E :                                               ULONG* memory_size) {
  89  E :    for (; next_memory_range_index_ < memory_ranges_->size();
  90  E :         ++next_memory_range_index_) {
  91    :      // A zero-length range will terminate memory callbacks. If there is one in
  92    :      // our input vector, skip it.
  93  E :      if ((*memory_ranges_)[next_memory_range_index_].size() == 0)
  94  i :        continue;
  95    :  
  96    :      // Include the specified memory region.
  97  E :      *memory_base = (*memory_ranges_)[next_memory_range_index_].start();
  98  E :      *memory_size = (*memory_ranges_)[next_memory_range_index_].size();
  99  E :      ++next_memory_range_index_;
 100  E :      return TRUE;
 101  i :    }
 102  E :    return FALSE;
 103  E :  }
 104    :  
 105    :  // static
 106    :  BOOL CALLBACK MinidumpCallbackHandler::CallbackRoutine(
 107    :      PVOID context,
 108    :      const PMINIDUMP_CALLBACK_INPUT callback_input,
 109  E :      PMINIDUMP_CALLBACK_OUTPUT callback_output) {
 110    :    MinidumpCallbackHandler* self =
 111  E :        reinterpret_cast<MinidumpCallbackHandler*>(context);
 112  E :    switch (callback_input->CallbackType) {
 113    :      case ::MemoryCallback:
 114    :        return self->MemoryCallback(&callback_output->MemoryBase,
 115  E :                                    &callback_output->MemorySize);
 116    :  
 117    :      // Include all modules.
 118    :      case IncludeModuleCallback:
 119    :      case ModuleCallback:
 120  E :        return TRUE;
 121    :  
 122    :      // Include all threads.
 123    :      case IncludeThreadCallback:
 124    :      case ThreadCallback:
 125  E :        return TRUE;
 126    :  
 127    :      // Stop receiving cancel callbacks.
 128    :      case CancelCallback:
 129  E :        callback_output->CheckCancel = FALSE;
 130  E :        callback_output->Cancel = FALSE;
 131  E :        return TRUE;
 132    :    }
 133    :    // Ignore other callback types.
 134  E :    return FALSE;
 135  E :  }
 136    :  
 137    :  // Checks that the range lives in a readable section of the module.
 138    :  bool VerifyRangeInModule(HMODULE module,
 139  E :                           const kasko::MinidumpRequest::MemoryRange& range) {
 140  E :    base::win::PEImage module_image(module);
 141    :    IMAGE_SECTION_HEADER* section = module_image.GetImageSectionFromAddr(
 142  E :        reinterpret_cast<void*>(range.start()));
 143    :  
 144    :    // If no section was returned, then the range doesn't reside in the module.
 145  E :    if (!section)
 146  i :      return false;
 147    :  
 148    :    // Make sure the range is in a readable section.
 149  E :    if ((section->Characteristics & IMAGE_SCN_MEM_READ) != IMAGE_SCN_MEM_READ)
 150  i :      return false;
 151    :  
 152    :    kasko::MinidumpRequest::MemoryRange section_range(
 153    :        reinterpret_cast<uint32_t>(
 154    :            module_image.RVAToAddr(section->VirtualAddress)),
 155  E :        section->SizeOfRawData);
 156  E :    return section_range.Contains(range);
 157  E :  }
 158    :  
 159    :  void AppendLoaderLockMemoryRanges(
 160  E :      std::vector<kasko::MinidumpRequest::MemoryRange>* memory_ranges) {
 161  E :    DCHECK(memory_ranges);
 162    :  
 163  E :    CRITICAL_SECTION* loader_lock = GetLoaderLock();
 164    :  
 165    :    // Add the range for the loader lock. This works because ntdll is loaded at
 166    :    // the same address in all processes.
 167    :    kasko::MinidumpRequest::MemoryRange loader_lock_memory_range(
 168  E :        reinterpret_cast<uint32_t>(loader_lock), sizeof(CRITICAL_SECTION));
 169  E :    memory_ranges->push_back(loader_lock_memory_range);
 170    :  
 171    :    // Add range for loader lock debuginfo. Dereferencing the loader lock is
 172    :    // required so a basic check is performed first. The loader lock should always
 173    :    // be living in ntdll globals and in a readable section.
 174  E :    HMODULE ntdll_module = ::GetModuleHandle(L"ntdll.dll");
 175  E :    if (VerifyRangeInModule(ntdll_module, loader_lock_memory_range)) {
 176    :      kasko::MinidumpRequest::MemoryRange debug_info_memory_range(
 177    :          reinterpret_cast<uint32_t>(loader_lock->DebugInfo),
 178  E :          sizeof(CRITICAL_SECTION_DEBUG));
 179  E :      memory_ranges->push_back(debug_info_memory_range);
 180  E :      DCHECK(VerifyRangeInModule(ntdll_module, debug_info_memory_range));
 181    :    }
 182  E :  }
 183    :  
 184    :  std::vector<kasko::MinidumpRequest::MemoryRange> AugmentMemoryRanges(
 185  E :      const std::vector<kasko::MinidumpRequest::MemoryRange>* memory_ranges) {
 186    :    std::vector<kasko::MinidumpRequest::MemoryRange> augmented_memory_ranges(
 187  E :        *memory_ranges);
 188    :  
 189  E :    AppendLoaderLockMemoryRanges(&augmented_memory_ranges);
 190    :  
 191  E :    return augmented_memory_ranges;
 192  E :  }
 193    :  
 194  E :  DWORD GetRequiredAccessForMinidumpTypeImpl(bool is_full_type) {
 195  E :    DWORD required_access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
 196    :  
 197  E :    if (is_full_type) {
 198    :      // A full dump includes handle data (MiniDumpWithHandleData).
 199  i :      required_access |= PROCESS_DUP_HANDLE;
 200    :    }
 201    :  
 202  E :    return required_access;
 203  E :  }
 204    :  
 205    :  }  // namespace
 206    :  
 207  E :  DWORD GetRequiredAccessForMinidumpType(MinidumpRequest::Type type) {
 208    :    return GetRequiredAccessForMinidumpTypeImpl(type ==
 209  E :                                                MinidumpRequest::FULL_DUMP_TYPE);
 210  E :  }
 211    :  
 212  E :  DWORD GetRequiredAccessForMinidumpType(api::MinidumpType type) {
 213  E :    return GetRequiredAccessForMinidumpTypeImpl(type == api::FULL_DUMP_TYPE);
 214  E :  }
 215    :  
 216    :  bool GenerateMinidump(const base::FilePath& destination,
 217    :                        base::ProcessHandle target_process,
 218    :                        base::PlatformThreadId thread_id,
 219  E :                        const MinidumpRequest& request) {
 220  E :    MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = nullptr;
 221    :    MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
 222    :  
 223  E :    if (request.exception_info_address) {
 224  E :      dump_exception_info.ThreadId = thread_id;
 225    :      dump_exception_info.ExceptionPointers =
 226  E :          reinterpret_cast<PEXCEPTION_POINTERS>(request.exception_info_address);
 227  E :      dump_exception_info.ClientPointers = request.client_exception_pointers;
 228    :  
 229  E :      dump_exception_pointers = &dump_exception_info;
 230    :    }
 231    :  
 232    :    base::File destination_file(destination, static_cast<base::File::Flags>(
 233    :                                                 base::File::FLAG_CREATE_ALWAYS |
 234  E :                                                 base::File::FLAG_WRITE));
 235  E :    if (!destination_file.IsValid()) {
 236  E :      LOG(ERROR) << "Failed to create destination file: " << destination.value();
 237  E :      return false;
 238    :    }
 239    :  
 240  E :    MINIDUMP_TYPE platform_minidump_type = kSmallDumpType;
 241    :  
 242  E :    switch (request.type) {
 243    :      case MinidumpRequest::SMALL_DUMP_TYPE:
 244  E :        platform_minidump_type = kSmallDumpType;
 245  E :        break;
 246    :      case MinidumpRequest::LARGER_DUMP_TYPE:
 247  E :        platform_minidump_type = kLargerDumpType;
 248  E :        break;
 249    :      case MinidumpRequest::FULL_DUMP_TYPE:
 250  E :        platform_minidump_type = kFullDumpType;
 251  E :        break;
 252    :      default:
 253  i :        NOTREACHED();
 254    :        break;
 255    :    }
 256    :  
 257  E :    std::vector<MINIDUMP_USER_STREAM> user_streams;
 258  E :    for (const auto& custom_stream : request.custom_streams) {
 259    :      MINIDUMP_USER_STREAM user_stream = {custom_stream.type,
 260    :                                          custom_stream.length,
 261  E :                                          const_cast<void*>(custom_stream.data)};
 262  E :      user_streams.push_back(user_stream);
 263  E :    }
 264    :  
 265    :    MINIDUMP_USER_STREAM_INFORMATION
 266  E :    user_stream_information = {user_streams.size(), user_streams.data()};
 267    :  
 268    :    // Add loader lock to the memory_ranges.
 269    :    std::vector<kasko::MinidumpRequest::MemoryRange> augmented_memory_ranges =
 270  E :        AugmentMemoryRanges(&request.user_selected_memory_ranges);
 271    :  
 272  E :    MinidumpCallbackHandler callback_handler(&augmented_memory_ranges);
 273    :  
 274    :    if (::MiniDumpWriteDump(
 275    :            target_process, base::GetProcId(target_process),
 276    :            destination_file.GetPlatformFile(), platform_minidump_type,
 277    :            dump_exception_pointers, &user_stream_information,
 278    :            const_cast<MINIDUMP_CALLBACK_INFORMATION*>(
 279  E :                callback_handler.GetMINIDUMP_CALLBACK_INFORMATION())) == FALSE) {
 280  i :      LOG(ERROR) << "MiniDumpWriteDump failed: " << ::common::LogWe() << ".";
 281  i :      return false;
 282    :    }
 283    :  
 284  E :    return true;
 285  E :  }
 286    :  
 287    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.