diff options
author | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 21:09:02 +0000 |
---|---|---|
committer | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 21:09:02 +0000 |
commit | d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60 (patch) | |
tree | d4fca3d3c24d56a325000a4129d11c35b548abfa /chrome_frame/function_stub.cc | |
parent | 722ed48734460567be2c97590a036480c4667815 (diff) | |
download | chromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.zip chromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.tar.gz chromium_src-d999c7c3f4bd9c7c779b4c06ee148d3d0d227e60.tar.bz2 |
Reimplementation of FunctionStub to avoid rewriting potentially executing code for a slight improvement in thread safety.
Make VTABLE patching treadsafe to the extent possible. As-is it's now safe against itself running on other threads at lease, as well as against other similar implementations, though the inherent VM operation race is resolved by retrying.
BUG=27415
TEST=Included unittests.
Review URL: http://codereview.chromium.org/992008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42381 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/function_stub.cc')
-rw-r--r-- | chrome_frame/function_stub.cc | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/chrome_frame/function_stub.cc b/chrome_frame/function_stub.cc new file mode 100644 index 0000000..59a4029 --- /dev/null +++ b/chrome_frame/function_stub.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2009 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/function_stub.h" + +#include <new> +#include "base/lock.h" +#include "base/logging.h" + +#ifndef _M_IX86 +#error Only x86 supported right now. +#endif + +namespace { +typedef enum AsmConstants { + POP_EAX = 0x58, + PUSH_IND = 0x35ff, + PUSH_EAX = 0x50, + JUMP_IND = 0x25ff, +}; + +// A quick and dirty wrapper class that allows us to defer allocating +// the executable heap until first use, and to release it teardown. +class ExecutableHeap { + public: + ExecutableHeap() : heap_(NULL) { + } + + ~ExecutableHeap() { + if (heap_ != NULL) { + BOOL ret = ::HeapDestroy(heap_); + heap_ = NULL; + } + } + + void* Allocate(size_t size) { + if (!heap_) + CreateHeap(); + + DCHECK(heap_); + + return ::HeapAlloc(heap_, 0, size); + } + + void Free(void* ptr) { + DCHECK(heap_ != NULL); + ::HeapFree(heap_, 0, ptr); + } + + void CreateHeap() { + AutoLock lock(init_lock_); + + if (heap_ == NULL) + heap_ = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); + } + + private: + Lock init_lock_; + HANDLE heap_; +}; + +// Our executable heap instance, all stubs are allocated from here. +ExecutableHeap heap_; + +} // namespace + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +bool FunctionStub::is_valid() const { + return signature_ == reinterpret_cast<HMODULE>(&__ImageBase) && + !is_bypassed(); +} + +FunctionStub::FunctionStub(uintptr_t extra_argument, void* dest) + : signature_(reinterpret_cast<HMODULE>(&__ImageBase)), + argument_(extra_argument), + destination_function_(reinterpret_cast<uintptr_t>(dest)) { + bypass_address_ = reinterpret_cast<uintptr_t>(&stub_.pop_return_addr_); + Init(&stub_); +} + +FunctionStub::~FunctionStub() { +} + +void FunctionStub::Init(FunctionStubAsm* stub) { + DCHECK(stub != NULL); + + stub->jump_to_bypass_ = JUMP_IND; + stub->bypass_target_addr_ = reinterpret_cast<uintptr_t>(&bypass_address_); + stub->pop_return_addr_ = POP_EAX; + stub->push_ = PUSH_IND; + stub->arg_addr_ = reinterpret_cast<uintptr_t>(&argument_); + stub->push_return_addr_ = PUSH_EAX; + stub->jump_to_target = JUMP_IND; + stub->target_addr_ = reinterpret_cast<uintptr_t>(&destination_function_); + + // Flush the instruction cache for the newly written code. + BOOL ret = ::FlushInstructionCache(::GetCurrentProcess(), + stub, + sizeof(*stub)); +} + +void FunctionStub::BypassStub(void* new_target) { + set_bypass_address(reinterpret_cast<uintptr_t>(new_target)); +} + +FunctionStub* FunctionStub::Create(uintptr_t extra_argument, void* dest) { + DCHECK(dest); + FunctionStub* stub = + reinterpret_cast<FunctionStub*>(heap_.Allocate(sizeof(FunctionStub))); + + if (stub != NULL) + new (stub) FunctionStub(extra_argument, dest); + + return stub; +} + +FunctionStub* FunctionStub::FromCode(void* address) { + // Address points to arbitrary code here, which may e.g. + // lie at the end of an executable segment, which in turn + // may terminate earlier than the last address we probe. + // We therefore execute under an SEH, so as not to crash + // on failed probes. + __try { + // Retrieve the candidata function stub. + FunctionStub* candidate = CONTAINING_RECORD(address, FunctionStub, stub_); + if (candidate->stub_.jump_to_bypass_ == JUMP_IND && + candidate->signature_ == reinterpret_cast<HMODULE>(&__ImageBase)) { + return candidate; + } + } __except(EXCEPTION_EXECUTE_HANDLER) { + } + + return NULL; +} + +bool FunctionStub::Destroy(FunctionStub* stub) { + heap_.Free(stub); + + return true; +} |