| Index: chrome_frame/vtable_patch_manager.cc
|
| diff --git a/chrome_frame/vtable_patch_manager.cc b/chrome_frame/vtable_patch_manager.cc
|
| deleted file mode 100644
|
| index da1bbabbff6ad7fa8748a35e38fc79e7084cb0c6..0000000000000000000000000000000000000000
|
| --- a/chrome_frame/vtable_patch_manager.cc
|
| +++ /dev/null
|
| @@ -1,244 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome_frame/vtable_patch_manager.h"
|
| -
|
| -#include <atlcomcli.h>
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/atomicops.h"
|
| -#include "base/logging.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/synchronization/lock.h"
|
| -#include "chrome_frame/function_stub.h"
|
| -#include "chrome_frame/pin_module.h"
|
| -
|
| -namespace vtable_patch {
|
| -
|
| -// The number of times we retry a patch/unpatch operation in case of
|
| -// VM races with other 3rd party software trying to patch the same thing.
|
| -const int kMaxRetries = 3;
|
| -
|
| -// We hold a lock over all patching operations to make sure that we don't
|
| -// e.g. race on VM operations to the same patches, or to physical pages
|
| -// shared across different VTABLEs.
|
| -base::Lock patch_lock_;
|
| -
|
| -namespace internal {
|
| -// Because other parties in our process might be attempting to patch the same
|
| -// virtual tables at the same time, we have a race to modify the VM protections
|
| -// on the pages. We also need to do a compare/swap type operation when we
|
| -// modify the function, so as to be sure that we grab the most recent value.
|
| -// Hence the SEH blocks and the nasty-looking compare/swap operation.
|
| -bool ReplaceFunctionPointer(void** entry, void* new_proc, void* curr_proc) {
|
| - __try {
|
| - base::subtle::Atomic32 prev_value;
|
| -
|
| - prev_value = base::subtle::NoBarrier_CompareAndSwap(
|
| - reinterpret_cast<base::subtle::Atomic32 volatile*>(entry),
|
| - reinterpret_cast<base::subtle::Atomic32>(curr_proc),
|
| - reinterpret_cast<base::subtle::Atomic32>(new_proc));
|
| -
|
| - return curr_proc == reinterpret_cast<void*>(prev_value);
|
| - } __except(EXCEPTION_EXECUTE_HANDLER) {
|
| - // Oops, we took exception on access.
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// Convenient definition of a VTABLE
|
| -typedef PROC* Vtable;
|
| -
|
| -// Returns a pointer to the VTable of a COM interface.
|
| -// @param unknown [in] The pointer of the COM interface.
|
| -inline Vtable GetIFVTable(void* unknown) {
|
| - return reinterpret_cast<Vtable>(*reinterpret_cast<void**>(unknown));
|
| -}
|
| -
|
| -HRESULT PatchInterfaceMethods(void* unknown, MethodPatchInfo* patches) {
|
| - // Do some sanity checking of the input arguments.
|
| - if (NULL == unknown || NULL == patches) {
|
| - NOTREACHED();
|
| - return E_INVALIDARG;
|
| - }
|
| -
|
| - Vtable vtable = GetIFVTable(unknown);
|
| - DCHECK(vtable);
|
| -
|
| - // All VM operations, patching and manipulation of MethodPatchInfo
|
| - // is done under a global lock, to ensure multiple threads don't
|
| - // race, whether on an individual patch, or on VM operations to
|
| - // the same physical pages.
|
| - base::AutoLock lock(patch_lock_);
|
| -
|
| - for (MethodPatchInfo* it = patches; it->index_ != -1; ++it) {
|
| - if (it->stub_ != NULL) {
|
| - // If this DCHECK fires it means that we are using the same VTable
|
| - // information to patch two different interfaces, or we've lost a
|
| - // race with another thread who's patching the same interface.
|
| - DLOG(WARNING) << "Attempting to patch two different VTables with the "
|
| - "same VTable information, or patching the same interface on "
|
| - "multiple threads";
|
| - continue;
|
| - }
|
| -
|
| - PROC original_fn = vtable[it->index_];
|
| - FunctionStub* stub = NULL;
|
| -
|
| -#ifndef NDEBUG
|
| - stub = FunctionStub::FromCode(original_fn);
|
| - if (stub != NULL) {
|
| - DLOG(ERROR) << "attempt to patch a function that's already patched";
|
| - DCHECK(stub->destination_function() ==
|
| - reinterpret_cast<uintptr_t>(it->method_)) <<
|
| - "patching the same method multiple times with different hooks?";
|
| - continue;
|
| - }
|
| -#endif
|
| -
|
| - stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(original_fn),
|
| - it->method_);
|
| - if (!stub) {
|
| - NOTREACHED();
|
| - return E_OUTOFMEMORY;
|
| - }
|
| -
|
| - // Do the VM operations and the patching in a loop, to try and ensure
|
| - // we succeed even if there's a VM operation or a patch race against
|
| - // other 3rd parties patching.
|
| - bool succeeded = false;
|
| - for (int i = 0; !succeeded && i < kMaxRetries; ++i) {
|
| - DWORD protect = 0;
|
| - if (!::VirtualProtect(&vtable[it->index_], sizeof(PROC),
|
| - PAGE_EXECUTE_READWRITE, &protect)) {
|
| - HRESULT hr = AtlHresultFromLastError();
|
| - DLOG(ERROR) << "VirtualProtect failed 0x" << std::hex << hr;
|
| -
|
| - // Go around again in the feeble hope that this is
|
| - // a temporary problem.
|
| - continue;
|
| - }
|
| - original_fn = vtable[it->index_];
|
| - stub->set_argument(reinterpret_cast<uintptr_t>(original_fn));
|
| - succeeded = internal::ReplaceFunctionPointer(
|
| - reinterpret_cast<void**>(&vtable[it->index_]), stub->code(),
|
| - original_fn);
|
| -
|
| - if (!::VirtualProtect(&vtable[it->index_], sizeof(PROC), protect,
|
| - &protect)) {
|
| - DLOG(ERROR) << "VirtualProtect failed to restore protection";
|
| - }
|
| - }
|
| -
|
| - if (!succeeded) {
|
| - FunctionStub::Destroy(stub);
|
| - stub = NULL;
|
| -
|
| - DLOG(ERROR) << "Failed to patch VTable.";
|
| - return E_FAIL;
|
| - } else {
|
| - // Success, save the stub we created.
|
| - it->stub_ = stub;
|
| - chrome_frame::PinModule();
|
| - }
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT UnpatchInterfaceMethods(MethodPatchInfo* patches) {
|
| - base::AutoLock lock(patch_lock_);
|
| -
|
| - for (MethodPatchInfo* it = patches; it->index_ != -1; ++it) {
|
| - if (it->stub_) {
|
| - DCHECK(it->stub_->destination_function() ==
|
| - reinterpret_cast<uintptr_t>(it->method_));
|
| - // Modify the stub to just jump directly to the original function.
|
| - it->stub_->BypassStub(reinterpret_cast<void*>(it->stub_->argument()));
|
| - it->stub_ = NULL;
|
| - // Leave the stub in memory so that we won't break any possible chains.
|
| -
|
| - // TODO(siggi): why not restore the original VTBL pointer here, provided
|
| - // we haven't been chained?
|
| - } else {
|
| - DLOG(WARNING) << "attempt to unpatch a function that wasn't patched";
|
| - }
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Disabled for now as we're not using it atm.
|
| -#if 0
|
| -
|
| -DynamicPatchManager::DynamicPatchManager(const MethodPatchInfo* patch_prototype)
|
| - : patch_prototype_(patch_prototype) {
|
| - DCHECK(patch_prototype_);
|
| - DCHECK(patch_prototype_->stub_ == NULL);
|
| -}
|
| -
|
| -DynamicPatchManager::~DynamicPatchManager() {
|
| - UnpatchAll();
|
| -}
|
| -
|
| -HRESULT DynamicPatchManager::PatchObject(void* unknown) {
|
| - int patched_methods = 0;
|
| - for (; patch_prototype_[patched_methods].index_ != -1; patched_methods++) {
|
| - // If you hit this, then you are likely using the prototype instance for
|
| - // patching in _addition_ to this class. This is not a good idea :)
|
| - DCHECK(patch_prototype_[patched_methods].stub_ == NULL);
|
| - }
|
| -
|
| - // Prepare a new patch object using the patch info from the prototype.
|
| - int mem_size = sizeof(PatchedObject) +
|
| - sizeof(MethodPatchInfo) * patched_methods;
|
| - PatchedObject* entry = reinterpret_cast<PatchedObject*>(new char[mem_size]);
|
| - entry->vtable_ = GetIFVTable(unknown);
|
| - memcpy(entry->patch_info_, patch_prototype_,
|
| - sizeof(MethodPatchInfo) * (patched_methods + 1));
|
| -
|
| - patch_list_lock_.Acquire();
|
| -
|
| - // See if we've already patched this vtable before.
|
| - // The search is done via the == operator of the PatchedObject class.
|
| - PatchList::const_iterator it = std::find(patch_list_.begin(),
|
| - patch_list_.end(), entry);
|
| - HRESULT hr;
|
| - if (it == patch_list_.end()) {
|
| - hr = PatchInterfaceMethods(unknown, entry->patch_info_);
|
| - if (SUCCEEDED(hr)) {
|
| - patch_list_.push_back(entry);
|
| - entry = NULL; // Ownership transferred to the array.
|
| - }
|
| - } else {
|
| - hr = S_FALSE;
|
| - }
|
| -
|
| - patch_list_lock_.Release();
|
| -
|
| - delete entry;
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -bool DynamicPatchManager::UnpatchAll() {
|
| - patch_list_lock_.Acquire();
|
| - PatchList::iterator it;
|
| - for (it = patch_list_.begin(); it != patch_list_.end(); it++) {
|
| - UnpatchInterfaceMethods((*it)->patch_info_);
|
| - delete (*it);
|
| - }
|
| - patch_list_.clear();
|
| - patch_list_lock_.Release();
|
| -
|
| - return true;
|
| -}
|
| -
|
| -#endif // disabled DynamicPatchManager
|
| -
|
| -} // namespace vtable_patch
|
|
|