Skip to content

Commit

Permalink
Bug 1432653: Refactor the DLL interceptor and parameterize its memory…
Browse files Browse the repository at this point in the history
… operations; r=handyman

MozReview-Commit-ID: EYxVsQ1kicy
  • Loading branch information
dblohm7 committed Apr 9, 2018
1 parent 8dce1af commit de9a291
Show file tree
Hide file tree
Showing 17 changed files with 2,876 additions and 1,508 deletions.
8 changes: 6 additions & 2 deletions dom/plugins/ipc/FunctionHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ FunctionHook::HookFunctions(int aQuirks)
// This cache is created when a DLL is registered with a FunctionHook.
// It is cleared on a call to ClearDllInterceptorCache(). It
// must be freed before exit to avoid leaks.
typedef nsClassHashtable<nsCStringHashKey, WindowsDllInterceptor> DllInterceptors;
typedef nsClassHashtable<nsStringHashKey, WindowsDllInterceptor> DllInterceptors;
DllInterceptors* sDllInterceptorCache = nullptr;

WindowsDllInterceptor*
Expand All @@ -68,9 +68,13 @@ FunctionHook::GetDllInterceptorFor(const char* aModuleName)
sDllInterceptorCache = new DllInterceptors();
}

MOZ_ASSERT(NS_IsAscii(aModuleName), "Non-ASCII module names are not supported");
NS_ConvertASCIItoUTF16 moduleName(aModuleName);

WindowsDllInterceptor* ret =
sDllInterceptorCache->LookupOrAdd(nsCString(aModuleName), aModuleName);
sDllInterceptorCache->LookupOrAdd(moduleName);
MOZ_ASSERT(ret);
ret->Init(moduleName.get());
return ret;
}

Expand Down
224 changes: 224 additions & 0 deletions mozglue/misc/interceptor/MMPolicies.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef mozilla_interceptor_MMPolicies_h
#define mozilla_interceptor_MMPolicies_h

#include "mozilla/Assertions.h"
#include "mozilla/Types.h"

#include <windows.h>

namespace mozilla {
namespace interceptor {

class MMPolicyBase
{
public:
static DWORD ComputeAllocationSize(const uint32_t aRequestedSize)
{
MOZ_ASSERT(aRequestedSize);
DWORD result = aRequestedSize;

const uint32_t granularity = GetAllocGranularity();

uint32_t mod = aRequestedSize % granularity;
if (mod) {
result += (granularity - mod);
}

return result;
}

static DWORD GetAllocGranularity()
{
static const DWORD kAllocGranularity = []() -> DWORD {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
return sysInfo.dwAllocationGranularity;
}();

return kAllocGranularity;
}

static DWORD GetPageSize()
{
static const DWORD kPageSize = []() -> DWORD {
SYSTEM_INFO sysInfo;
::GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize;
}();

return kPageSize;
}
};

class MMPolicyInProcess : public MMPolicyBase
{
public:
typedef MMPolicyInProcess MMPolicyT;

explicit MMPolicyInProcess()
: mBase(nullptr)
, mReservationSize(0)
, mCommitOffset(0)
{
}

MMPolicyInProcess(const MMPolicyInProcess&) = delete;
MMPolicyInProcess& operator=(const MMPolicyInProcess&) = delete;

MMPolicyInProcess(MMPolicyInProcess&& aOther)
: mBase(nullptr)
, mReservationSize(0)
, mCommitOffset(0)
{
*this = Move(aOther);
}

MMPolicyInProcess& operator=(MMPolicyInProcess&& aOther)
{
mBase = aOther.mBase;
aOther.mBase = nullptr;

mCommitOffset = aOther.mCommitOffset;
aOther.mCommitOffset = 0;

mReservationSize = aOther.mReservationSize;
aOther.mReservationSize = 0;

return *this;
}

// We always leak mBase
~MMPolicyInProcess() = default;

explicit operator bool() const
{
return !!mBase;
}

/**
* Should we unhook everything upon destruction?
*/
bool ShouldUnhookUponDestruction() const
{
return true;
}

bool Read(void* aToPtr, const void* aFromPtr, size_t aLen) const
{
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}

bool Write(void* aToPtr, const void* aFromPtr, size_t aLen) const
{
::memcpy(aToPtr, aFromPtr, aLen);
return true;
}

bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags,
uint32_t* aPrevProtFlags) const
{
MOZ_ASSERT(aPrevProtFlags);
BOOL ok = ::VirtualProtect(aVAddress, aSize, aProtFlags,
reinterpret_cast<PDWORD>(aPrevProtFlags));
MOZ_ASSERT(ok);
return !!ok;
}

/**
* @return true if the page that hosts aVAddress is accessible.
*/
bool IsPageAccessible(void* aVAddress) const
{
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = ::VirtualQuery(aVAddress, &mbi, sizeof(mbi));

return result && mbi.AllocationProtect && mbi.State == MEM_COMMIT &&
mbi.Protect != PAGE_NOACCESS;
}

bool FlushInstructionCache() const
{
return !!::FlushInstructionCache(::GetCurrentProcess(), nullptr, 0);
}

protected:
uint8_t* GetLocalView() const
{
return mBase;
}

uintptr_t GetRemoteView() const
{
// Same as local view for in-process
return reinterpret_cast<uintptr_t>(mBase);
}

/**
* @return the effective number of bytes reserved, or 0 on failure
*/
uint32_t Reserve(const uint32_t aSize)
{
if (!aSize) {
return 0;
}

if (mBase) {
MOZ_ASSERT(mReservationSize >= aSize);
return mReservationSize;
}

mReservationSize = ComputeAllocationSize(aSize);
mBase = static_cast<uint8_t*>(::VirtualAlloc(nullptr, mReservationSize,
MEM_RESERVE, PAGE_NOACCESS));
if (!mBase) {
return 0;
}
return mReservationSize;
}

bool MaybeCommitNextPage(const uint32_t aRequestedOffset,
const uint32_t aRequestedLength)
{
if (!(*this)) {
return false;
}

uint32_t limit = aRequestedOffset + aRequestedLength - 1;
if (limit < mCommitOffset) {
// No commit required
return true;
}

MOZ_DIAGNOSTIC_ASSERT(mCommitOffset < mReservationSize);
if (mCommitOffset >= mReservationSize) {
return false;
}

PVOID local = ::VirtualAlloc(mBase + mCommitOffset, GetPageSize(),
MEM_COMMIT, PAGE_EXECUTE_READ);
if (!local) {
return false;
}

mCommitOffset += GetPageSize();
return true;
}

private:
uint8_t* mBase;
uint32_t mReservationSize;
uint32_t mCommitOffset;
};

} // namespace interceptor
} // namespace mozilla

#endif // mozilla_interceptor_MMPolicies_h

84 changes: 84 additions & 0 deletions mozglue/misc/interceptor/PatcherBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef mozilla_interceptor_PatcherBase_h
#define mozilla_interceptor_PatcherBase_h

#include "mozilla/interceptor/TargetFunction.h"

namespace mozilla {
namespace interceptor {

template <typename VMPolicy>
class WindowsDllPatcherBase
{
protected:
typedef typename VMPolicy::MMPolicyT MMPolicyT;

template <typename... Args>
explicit WindowsDllPatcherBase(Args... aArgs)
: mVMPolicy(mozilla::Forward<Args>(aArgs)...)
{
}

ReadOnlyTargetFunction<MMPolicyT>
ResolveRedirectedAddress(const void* aOriginalFunction)
{
ReadOnlyTargetFunction<MMPolicyT> origFn(mVMPolicy, aOriginalFunction);
// If function entry is jmp rel8 stub to the internal implementation, we
// resolve redirected address from the jump target.
if (origFn[0] == 0xeb) {
int8_t offset = (int8_t)(origFn[1]);
if (offset <= 0) {
// Bail out for negative offset: probably already patched by some
// third-party code.
return Move(origFn);
}

for (int8_t i = 0; i < offset; i++) {
if (origFn[2 + i] != 0x90) {
// Bail out on insufficient nop space.
return Move(origFn);
}
}

origFn += 2 + offset;
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, origFn.GetAddress());
}

#if defined(_M_IX86)
// If function entry is jmp [disp32] such as used by kernel32,
// we resolve redirected address from import table.
if (origFn[0] == 0xff && origFn[1] == 0x25) {
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy,
reinterpret_cast<const void*>((origFn + 2).template ChasePointer<uintptr_t*>()));
}
#elif defined(_M_X64)
// If function entry is jmp [disp32] such as used by kernel32,
// we resolve redirected address from import table.
if (origFn[0] == 0x48 && origFn[1] == 0xff && origFn[2] == 0x25) {
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy,
reinterpret_cast<const void*>((origFn + 3).ChasePointerFromDisp()));
}

if (origFn[0] == 0xe9) {
// require for TestDllInterceptor with --disable-optimize
uintptr_t abstarget = (origFn + 1).ReadDisp32AsAbsolute();
return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, abstarget);
}
#endif

return Move(origFn);
}

protected:
VMPolicy mVMPolicy;
};

} // namespace interceptor
} // namespace mozilla

#endif // mozilla_interceptor_PatcherBase_h
Loading

0 comments on commit de9a291

Please sign in to comment.