Coverage for /Syzygy/refinery/analyzers/stack_analyzer_impl.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00234.C++source

Line-by-line coverage:

   1    :  // Copyright 2015 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/refinery/analyzers/stack_analyzer_impl.h"
  16    :  
  17    :  #include <algorithm>
  18    :  #include <string>
  19    :  #include <vector>
  20    :  
  21    :  #include "base/logging.h"
  22    :  #include "base/memory/scoped_ptr.h"
  23    :  #include "base/numerics/safe_conversions.h"
  24    :  #include "base/strings/string16.h"
  25    :  #include "base/strings/string_util.h"
  26    :  #include "base/strings/stringprintf.h"
  27    :  #include "base/strings/utf_string_conversions.h"
  28    :  #include "base/win/scoped_bstr.h"
  29    :  #include "base/win/scoped_comptr.h"
  30    :  #include "syzygy/common/com_utils.h"
  31    :  #include "syzygy/pe/dia_util.h"
  32    :  #include "syzygy/refinery/core/addressed_data.h"
  33    :  #include "syzygy/refinery/process_state/process_state.h"
  34    :  #include "syzygy/refinery/process_state/process_state_util.h"
  35    :  #include "syzygy/refinery/process_state/refinery.pb.h"
  36    :  
  37  m :  namespace refinery {
  38    :  
  39  m :  namespace {
  40    :  
  41  m :  bool GetDiaSession(ULONGLONG va,
  42  m :                     ProcessState* process_state,
  43  m :                     scoped_refptr<DiaSymbolProvider> symbol_provider,
  44  m :                     base::win::ScopedComPtr<IDiaSession>* session) {
  45  m :    DCHECK(process_state); DCHECK(session);
  46    :  
  47    :    // Get the module's signature.
  48  m :    ModuleLayerAccessor accessor(process_state);
  49  m :    pe::PEFile::Signature signature;
  50  m :    if (!accessor.GetModuleSignature(va, &signature))
  51  m :      return false;
  52    :  
  53    :    // Retrieve the session.
  54  m :    base::win::ScopedComPtr<IDiaSession> session_tmp;
  55  m :    if (!symbol_provider->FindOrCreateDiaSession(signature, &session_tmp))
  56  m :      return false;
  57    :  
  58    :    // Set the load address (the same module might be loaded at multiple VAs).
  59  m :    HRESULT hr = session_tmp->put_loadAddress(signature.base_address.value());
  60  m :    if (FAILED(hr)) {
  61  m :      LOG(ERROR) << "Unable to set session's load address: " << common::LogHr(hr);
  62  m :      return false;
  63  m :    }
  64  m :    *session = session_tmp;
  65  m :    return true;
  66  m :  }
  67    :  
  68  m :  }  // namespace
  69    :  
  70  m :  StackWalkHelper::StackWalkHelper(
  71  m :      scoped_refptr<DiaSymbolProvider> symbol_provider)
  72  m :      : symbol_provider_(symbol_provider) {
  73  m :    DCHECK(symbol_provider.get() != nullptr);
  74  m :  }
  75    :  
  76  m :  StackWalkHelper::~StackWalkHelper() {
  77  m :  }
  78    :  
  79  m :  void StackWalkHelper::SetState(StackRecordPtr stack_record,
  80  m :                                 ProcessState* process_state) {
  81    :    // Set up the context. For the excepting thread, we use the exception's
  82    :    // context.
  83    :    // TODO(manzagop): consider walking stack from all available contexts and
  84    :    // merging the walks.
  85  m :    registers_.clear();
  86  m :    DCHECK(stack_record->data().has_thread_info());
  87  m :    const ThreadInformation& thread_info = stack_record->data().thread_info();
  88  m :    const RegisterInformation* context;
  89  m :    if (thread_info.has_exception()) {
  90  m :      context = &thread_info.exception().register_info();
  91  m :    } else {
  92  m :      context = &thread_info.register_info();
  93  m :    }
  94    :  
  95    :    // Set registers that are handled.
  96    :    // TODO(manzagop): should the the allreg registers also be set?
  97  m :    put_registerValue(CV_REG_GS, context->seg_gs());
  98  m :    put_registerValue(CV_REG_FS, context->seg_fs());
  99  m :    put_registerValue(CV_REG_ES, context->seg_es());
 100  m :    put_registerValue(CV_REG_DS, context->seg_ds());
 101  m :    put_registerValue(CV_REG_EDI, context->edi());
 102  m :    put_registerValue(CV_REG_ESI, context->esi());
 103  m :    put_registerValue(CV_REG_EBX, context->ebx());
 104  m :    put_registerValue(CV_REG_EDX, context->edx());
 105  m :    put_registerValue(CV_REG_ECX, context->ecx());
 106  m :    put_registerValue(CV_REG_EAX, context->eax());
 107  m :    put_registerValue(CV_REG_EBP, context->ebp());
 108  m :    put_registerValue(CV_REG_EIP, context->eip());
 109  m :    put_registerValue(CV_REG_CS, context->seg_cs());
 110  m :    put_registerValue(CV_REG_EFLAGS, context->eflags());
 111  m :    put_registerValue(CV_REG_ESP, context->esp());
 112  m :    put_registerValue(CV_REG_SS, context->seg_ss());
 113    :  
 114    :    // Set the process state.
 115  m :    process_state_ = process_state;
 116  m :  }
 117    :  
 118  m :  STDMETHODIMP_(ULONG) StackWalkHelper::AddRef() {
 119  m :    return base::win::IUnknownImpl::AddRef();
 120  m :  }
 121    :  
 122  m :  STDMETHODIMP_(ULONG) StackWalkHelper::Release() {
 123  m :    return base::win::IUnknownImpl::Release();
 124  m :  }
 125    :  
 126  m :  STDMETHODIMP StackWalkHelper::QueryInterface(REFIID riid, PVOID* ptr_void) {
 127  m :    DCHECK(ptr_void);
 128    :  
 129  m :    if (riid == __uuidof(IDiaStackWalkHelper)) {
 130  m :      *ptr_void = static_cast<IDiaStackWalkHelper*>(this);
 131  m :      AddRef();
 132  m :      return S_OK;
 133  m :    } else {
 134    :      // Know it if another interface is requested.
 135  m :      const int kGUIDSize = 39;
 136  m :      std::wstring riid_str;
 137  m :      int result =
 138  m :          StringFromGUID2(riid, base::WriteInto(&riid_str, kGUIDSize), kGUIDSize);
 139  m :      DCHECK_GT(result, 0);
 140  m :      LOG(ERROR) << base::StringPrintf(L"StackWalkHelper::QueryInterface for %ls",
 141  m :                                       riid_str.c_str());
 142  m :      DCHECK(false);
 143  m :      return base::win::IUnknownImpl::QueryInterface(riid, ptr_void);
 144  m :    }
 145  m :  }
 146    :  
 147  m :  STDMETHODIMP StackWalkHelper::get_registerValue(DWORD index,
 148  m :                                                  ULONGLONG* pRetVal) {
 149  m :    if (!pRetVal) {
 150  m :      return E_INVALIDARG;
 151  m :    }
 152    :  
 153    :    // Only support retrieval of registers that were previously set.
 154  m :    const auto it = registers_.find(static_cast<CV_HREG_e>(index));
 155  m :    if (it != registers_.end()) {
 156  m :      *pRetVal = it->second;
 157  m :      return S_OK;
 158  m :    }
 159    :  
 160    :    // Even though this isn't in the function's contract, this ensures we'll pick
 161    :    // up on unexpected register retrieval attempts.
 162    :    // TODO(manzagop): add symbolic names for the registers.
 163  m :    LOG(ERROR) << base::StringPrintf("Failed to get register value (%u).", index);
 164  m :    DCHECK(false);
 165  m :    return E_FAIL;
 166  m :  }
 167    :  
 168  m :  STDMETHODIMP StackWalkHelper::put_registerValue(DWORD index, ULONGLONG NewVal) {
 169  m :    registers_[static_cast<CV_HREG_e>(index)] = NewVal;
 170  m :    return S_OK;
 171  m :  }
 172    :  
 173  m :  STDMETHODIMP StackWalkHelper::readMemory(MemoryTypeEnum unused_type,
 174  m :                                           ULONGLONG va,
 175  m :                                           DWORD cbData,
 176  m :                                           DWORD* pcbData,
 177  m :                                           BYTE* pbData) {
 178  m :    DCHECK(pcbData != NULL);
 179    :  
 180    :    // Handle the 0 size case.
 181  m :    if (cbData == 0) {
 182  m :      *pcbData = 0;
 183  m :      return S_OK;
 184  m :    }
 185    :  
 186    :    // Ensure range validity.
 187  m :    AddressRange range(va, cbData);
 188  m :    if (!range.IsValid()) {
 189  m :      LOG(ERROR) << "Invalid memory range.";
 190  m :      return E_FAIL;
 191  m :    }
 192    :  
 193    :    // Read from the backing process state.
 194  m :    size_t bytes_read = 0U;
 195  m :    if (process_state_->GetFrom(range, &bytes_read, pbData)) {
 196    :      // Note: this may only be a partial read.
 197  m :      *pcbData = bytes_read;
 198  m :      return S_OK;
 199  m :    }
 200    :  
 201    :    // If the memory comes from a module's range, attempt to service from the
 202    :    // module.
 203    :    // TODO(manzagop): success should depend on whether the module's memory
 204    :    // matches the requested memory type.
 205  m :    bytes_read = 0U;
 206  m :    if (ReadFromModule(range, &bytes_read, pbData)) {
 207  m :      VLOG(1) << "Servicing read from module. May not reflect actual memory.";
 208  m :      *pcbData = bytes_read;
 209  m :      return S_OK;
 210  m :    }
 211    :  
 212    :    // TODO(manzagop): introduce a function for logging VA to avoid crashing on
 213    :    // unexercised code when the error case occurs.
 214  m :    LOG(ERROR) << base::StringPrintf("\n Read failed (va: %08llx, size: %04x)",
 215  m :                                     va, cbData);
 216  m :    return E_FAIL;
 217  m :  }
 218    :  
 219  m :  STDMETHODIMP StackWalkHelper::searchForReturnAddress(IDiaFrameData* frame,
 220  m :                                                       ULONGLONG* returnAddress) {
 221  m :    return E_NOTIMPL;  // Use DIA's default search.
 222  m :  }
 223    :  
 224  m :  STDMETHODIMP StackWalkHelper::searchForReturnAddressStart(
 225  m :      IDiaFrameData* frame,
 226  m :      ULONGLONG startAddress,
 227  m :      ULONGLONG* returnAddress) {
 228  m :    return E_NOTIMPL;  // Use DIA's default search.
 229  m :  }
 230    :  
 231  m :  STDMETHODIMP StackWalkHelper::frameForVA(ULONGLONG va,
 232  m :                                           IDiaFrameData** frame_data) {
 233  m :    base::win::ScopedComPtr<IDiaSession> session;
 234  m :    if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
 235  m :      LOG(ERROR) << "Failed to get dia session.";
 236  m :      return E_FAIL;
 237  m :    }
 238    :  
 239    :    // Get the table that is a frame data enumerator.
 240  m :    base::win::ScopedComPtr<IDiaEnumFrameData> frame_enumerator;
 241  m :    pe::SearchResult result =
 242  m :        pe::FindDiaTable(__uuidof(IDiaEnumFrameData), session.get(),
 243  m :                         frame_enumerator.ReceiveVoid());
 244  m :    if (result != pe::kSearchSucceeded) {
 245  m :      LOG(ERROR) << "Failed to obtain frame data from the pdb.";
 246  m :      return E_FAIL;
 247  m :    }
 248    :  
 249    :    // Get the frame data.
 250  m :    HRESULT hr = frame_enumerator->frameByVA(va, frame_data);
 251  m :    if (hr != S_OK) {
 252  m :      if (hr == S_FALSE) {
 253  m :        LOG(ERROR) << "No frame data matches specified address.";
 254  m :      } else {
 255  m :        LOG(ERROR) << "Failed to get frame data.";
 256  m :      }
 257  m :    }
 258    :  
 259  m :    return hr;
 260  m :  }
 261    :  
 262  m :  STDMETHODIMP StackWalkHelper::symbolForVA(ULONGLONG va, IDiaSymbol** ppSymbol) {
 263  m :    base::win::ScopedComPtr<IDiaSession> session;
 264  m :    if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
 265  m :      LOG(ERROR) << "Failed to get dia session.";
 266  m :      return E_FAIL;
 267  m :    }
 268    :  
 269    :    // Search for a function.
 270  m :    base::win::ScopedComPtr<IDiaSymbol> function;
 271  m :    HRESULT hr = session->findSymbolByVA(va, SymTagFunction, function.Receive());
 272  m :    if (hr == S_OK) {
 273    :      // Get the associated function type.
 274  m :      base::win::ScopedComPtr<IDiaSymbol> function_type;
 275  m :      if (function->get_type(function_type.Receive()) != S_OK) {
 276  m :        LOG(ERROR) << "Failed to get function's type.";
 277  m :        return E_FAIL;
 278  m :      }
 279  m :      DWORD symtag = 0U;
 280  m :      DCHECK_EQ(S_OK, function_type->get_symTag(&symtag));
 281  m :      if (symtag == SymTagBaseType) {
 282    :        // We've observed a case of a function type that was a SymTagBaseType with
 283    :        // a base type of btNoType. Fail in this case.
 284  m :        LOG(ERROR) << "Function's type is not SymTagFunctionType.";
 285  m :        return E_FAIL;
 286  m :      }
 287  m :      DCHECK(symtag == SymTagFunctionType);
 288    :  
 289  m :      *ppSymbol = function_type.Detach();
 290  m :    } else {
 291    :      // Note: not having symbols is to be expected sometimes.
 292  m :      LOG(INFO) << base::StringPrintf("No symbols for VA (%08llx).", va);
 293  m :    }
 294    :  
 295  m :    return hr;
 296  m :  }
 297    :  
 298  m :  STDMETHODIMP StackWalkHelper::pdataForVA(ULONGLONG va,
 299  m :                                           DWORD cbData,
 300  m :                                           DWORD* pcbData,
 301  m :                                           BYTE* pbData) {
 302    :    // TODO(manzagop): implement to handle 64 bit stack walking.
 303  m :    return E_NOTIMPL;
 304  m :  }
 305    :  
 306  m :  STDMETHODIMP StackWalkHelper::imageForVA(ULONGLONG vaContext,
 307  m :                                           ULONGLONG* pvaImageStart) {
 308    :    // Get module's base address.
 309    :    // TODO(manzagop): set up indexing to optimize this.
 310  m :    ModuleRecordPtr module_record;
 311  m :    if (!process_state_->FindSingleRecord(vaContext, &module_record)) {
 312  m :      LOG(ERROR) << "Failed to find module for VA.";
 313  m :      return E_FAIL;
 314  m :    }
 315  m :    *pvaImageStart = module_record->range().start();
 316  m :    return S_OK;
 317  m :  }
 318    :  
 319  m :  STDMETHODIMP StackWalkHelper::addressForVA(ULONGLONG va,
 320  m :                                             _Out_ DWORD* pISect,
 321  m :                                             _Out_ DWORD* pOffset) {
 322  m :    base::win::ScopedComPtr<IDiaSession> session;
 323  m :    if (!GetDiaSession(va, process_state_, symbol_provider_, &session)) {
 324  m :      LOG(ERROR) << "Failed to get dia session.";
 325  m :      return E_FAIL;
 326  m :    }
 327    :  
 328  m :    HRESULT hr = session->addressForVA(va, pISect, pOffset);
 329  m :    if (hr != S_OK) {
 330  m :      LOG(ERROR) << "Failed to get address for va.";
 331  m :    }
 332    :  
 333  m :    return hr;
 334  m :  }
 335    :  
 336  m :  bool StackWalkHelper::ReadFromModule(const AddressRange& range,
 337  m :                                       size_t* bytes_read,
 338  m :                                       void* buffer) {
 339  m :    DCHECK(bytes_read);
 340  m :    *bytes_read = 0U;
 341    :  
 342    :    // Identify the module
 343  m :    ModuleRecordPtr module_record;
 344  m :    if (!process_state_->FindSingleRecord(range.start(), &module_record))
 345  m :      return false;
 346    :  
 347    :    // TODO(manzagop): actually implement the read instead of successfully
 348    :    // reading 0 bytes.
 349    :  
 350  m :    return true;
 351  m :  }
 352    :  
 353  m :  }  // namespace refinery

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