Bug 1532470: Part 4 - Add 4-byte patching to ARM64 interceptor; r=handyman

This patch modifies arm64 so that detours are peformed via two passes:
1. The first pass uses a null trampoline to count how many bytes are available
   for patching the original function.
2. If we have >= 16 bytes to patch, we reuse existing trampoline space. If we
   have less than 16 bytes to patch, we reserve trampoline space within 128MB
   of the function, allowing for a 4 byte patch.
3. Then we recurse, this time using a real trampoline.

Note that we still do a single-pass on x86(-64).

Differential Revision: https://phabricator.services.mozilla.com/D32193

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Aaron Klotz 2019-06-12 01:11:36 +00:00
Родитель 4460a190f9
Коммит e311b07d92
5 изменённых файлов: 462 добавлений и 100 удалений

Просмотреть файл

@ -17,10 +17,10 @@ struct PCRelativeLoadTest {
// The desired bits that we want to see after masking
uint32_t mMatchBits;
// If we match, mDecodeFn provide the code to decode the instruction.
LoadInfo (*mDecodeFn)(const uintptr_t aPC, const uint32_t aInst);
LoadOrBranch (*mDecodeFn)(const uintptr_t aPC, const uint32_t aInst);
};
static LoadInfo ADRPDecode(const uintptr_t aPC, const uint32_t aInst) {
static LoadOrBranch ADRPDecode(const uintptr_t aPC, const uint32_t aInst) {
// Keep in mind that on Windows aarch64, uint32_t is little-endian
const uint32_t kMaskDataProcImmPcRelativeImmLo = 0x60000000;
const uint32_t kMaskDataProcImmPcRelativeImmHi = 0x00FFFFE0;
@ -30,12 +30,19 @@ static LoadInfo ADRPDecode(const uintptr_t aPC, const uint32_t aInst) {
((aInst & kMaskDataProcImmPcRelativeImmHi) >> 3) |
((aInst & kMaskDataProcImmPcRelativeImmLo) >> 29),
21);
base &= ~0xFFFULL;
offset <<= 12;
uint8_t reg = aInst & 0x1F;
return LoadInfo(base + offset, reg);
return LoadOrBranch(base + offset, reg);
}
MFBT_API LoadOrBranch BUncondImmDecode(const uintptr_t aPC,
const uint32_t aInst) {
int32_t offset = SignExtend<int32_t>(aInst & 0x03FFFFFFU, 26);
return LoadOrBranch(aPC + offset);
}
// Order is important here; more specific encoding tests must be placed before
@ -60,8 +67,8 @@ static const PCRelativeLoadTest gPCRelTests[] = {
* present, then we call it to decode the instruction. If it is not present,
* then we assume that this particular instruction is unsupported.
*/
MFBT_API Result<LoadInfo, PCRelCheckError> CheckForPCRel(const uintptr_t aPC,
const uint32_t aInst) {
MFBT_API Result<LoadOrBranch, PCRelCheckError> CheckForPCRel(
const uintptr_t aPC, const uint32_t aInst) {
for (auto&& test : gPCRelTests) {
if ((aInst & test.mTestMask) == test.mMatchBits) {
if (!test.mDecodeFn) {

Просмотреть файл

@ -8,7 +8,11 @@
#define mozilla_interceptor_Arm64_h
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "mozilla/Saturate.h"
#include "mozilla/Types.h"
#include "mozilla/TypeTraits.h"
@ -16,17 +20,65 @@ namespace mozilla {
namespace interceptor {
namespace arm64 {
// This currently only handles loads, not branches
struct LoadInfo {
LoadInfo(const uintptr_t aAbsAddress, const uint8_t aDestReg)
: mAbsAddress(aAbsAddress), mDestReg(aDestReg) {
// clang-format off
enum class IntegerConditionCode : uint8_t {
// From the ARMv8 Architectural Reference Manual, Section C1.2.4
// Description Condition Flags
EQ = 0b0000, // == Z == 1
NE = 0b0001, // != Z == 0
CS = 0b0010, // carry set C == 1
HS = 0b0010, // carry set (alias) C == 1
CC = 0b0011, // carry clear C == 0
LO = 0b0011, // carry clear (alias) C == 0
MI = 0b0100, // < 0 N == 1
PL = 0b0101, // >= 0 N == 0
VS = 0b0110, // overflow V == 1
VC = 0b0111, // no overflow V == 0
HI = 0b1000, // unsigned > C == 1 && Z == 0
LS = 0b1001, // unsigned <= !(C == 1 && Z == 0)
GE = 0b1010, // signed >= N == V
LT = 0b1011, // signed < N != V
GT = 0b1100, // signed > Z == 0 && N == V
LE = 0b1101, // signed <= !(Z == 0 && N == V)
AL = 0b1110, // unconditional <Any>
NV = 0b1111 // unconditional (but AL is the preferred encoding)
};
// clang-format on
struct LoadOrBranch {
enum class Type {
Load,
Branch,
};
// Load constructor
LoadOrBranch(const uintptr_t aAbsAddress, const uint8_t aDestReg)
: mType(Type::Load), mAbsAddress(aAbsAddress), mDestReg(aDestReg) {
MOZ_ASSERT(aDestReg < 32);
}
// The absolute address to be loaded into a register
// Unconditional branch constructor
explicit LoadOrBranch(const uintptr_t aAbsAddress)
: mType(Type::Branch),
mAbsAddress(aAbsAddress),
mCond(IntegerConditionCode::AL) {}
// Conditional branch constructor
LoadOrBranch(const uintptr_t aAbsAddress, const IntegerConditionCode aCond)
: mType(Type::Branch), mAbsAddress(aAbsAddress), mCond(aCond) {}
Type mType;
// The absolute address to be loaded into a register, or branched to
uintptr_t mAbsAddress;
// The destination register for the load
uint8_t mDestReg;
union {
// The destination register for the load
uint8_t mDestReg;
// The condition code for the branch
IntegerConditionCode mCond;
};
};
enum class PCRelCheckError {
@ -34,8 +86,8 @@ enum class PCRelCheckError {
NoDecoderAvailable,
};
MFBT_API Result<LoadInfo, PCRelCheckError> CheckForPCRel(const uintptr_t aPC,
const uint32_t aInst);
MFBT_API Result<LoadOrBranch, PCRelCheckError> CheckForPCRel(
const uintptr_t aPC, const uint32_t aInst);
/**
* Casts |aValue| to a |ResultT| via sign extension.
@ -78,6 +130,90 @@ inline static uint32_t BuildUnconditionalBranchToRegister(const uint32_t aReg) {
return 0xD61F0000 | (aReg << 5);
}
MFBT_API LoadOrBranch BUncondImmDecode(const uintptr_t aPC,
const uint32_t aInst);
/**
* If |aTarget| is more than 128MB away from |aPC|, we need to use a veneer.
*/
inline static bool IsVeneerRequired(const uintptr_t aPC,
const uintptr_t aTarget) {
detail::Saturate<intptr_t> saturated(aTarget);
saturated -= aPC;
uintptr_t absDiff = Abs(saturated.value());
return absDiff >= 0x08000000U;
}
inline static bool IsUnconditionalBranchImm(const uint32_t aInst) {
return (aInst & 0xFC000000U) == 0x14000000U;
}
inline static Maybe<uint32_t> BuildUnconditionalBranchImm(
const uintptr_t aPC, const uintptr_t aTarget) {
detail::Saturate<intptr_t> saturated(aTarget);
saturated -= aPC;
CheckedInt<int32_t> offset(saturated.value());
if (!offset.isValid()) {
return Nothing();
}
// offset should be a multiple of 4
MOZ_ASSERT(offset.value() % 4 == 0);
if (offset.value() % 4) {
return Nothing();
}
offset /= 4;
if (!offset.isValid()) {
return Nothing();
}
int32_t signbits = offset.value() & 0xFE000000;
// Ensure that offset is small enough to fit into the 26 bit region.
// We check that the sign bits are either all ones or all zeros.
MOZ_ASSERT(signbits == 0xFE000000 || !signbits);
if (signbits && signbits != 0xFE000000) {
return Nothing();
}
int32_t masked = offset.value() & 0x03FFFFFF;
// B imm26
return Some(0x14000000U | masked);
}
/**
* Allocate and construct a veneer that provides an absolute 64-bit branch to
* the hook function.
*/
template <typename TrampPoolT>
inline static uintptr_t MakeVeneer(TrampPoolT& aTrampPool, void* aPrimaryTramp,
const uintptr_t aDestAddress) {
auto maybeVeneer = aTrampPool.GetNextTrampoline();
if (!maybeVeneer) {
return 0;
}
Trampoline<typename TrampPoolT::MMPolicyT> veneer(
std::move(maybeVeneer.ref()));
// Write the same header information that is used for trampolines
veneer.WriteEncodedPointer(nullptr);
veneer.WriteEncodedPointer(aPrimaryTramp);
veneer.StartExecutableCode();
// Register 16 is explicitly intended for veneers in ARM64, so we use that
// register without fear of clobbering anything important.
veneer.WriteLoadLiteral(aDestAddress, 16);
veneer.WriteInstruction(BuildUnconditionalBranchToRegister(16));
return reinterpret_cast<uintptr_t>(veneer.EndExecutableCode());
}
} // namespace arm64
} // namespace interceptor
} // namespace mozilla

Просмотреть файл

@ -12,7 +12,11 @@
#endif // defined(_M_ARM64)
#include "mozilla/interceptor/PatcherBase.h"
#include "mozilla/interceptor/Trampoline.h"
#include "mozilla/interceptor/VMSharingPolicies.h"
#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/NativeNt.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/Types.h"
@ -30,8 +34,8 @@ namespace interceptor {
enum class DetourFlags : uint32_t {
eDefault = 0,
eEnable10BytePatch = 1, // Allow 10-byte patches when conditions allow
eTestOnlyForce10BytePatch =
3, // Force 10-byte patches at all times (testing only)
eTestOnlyForceShortPatch =
2, // Force short patches at all times (x86-64 and arm64 testing only)
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DetourFlags)
@ -39,13 +43,18 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DetourFlags)
template <typename VMPolicy>
class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
typedef typename VMPolicy::MMPolicyT MMPolicyT;
DetourFlags mFlags;
typedef typename VMPolicy::PoolType TrampPoolT;
Maybe<DetourFlags> mFlags;
#if defined(_M_ARM64)
// LDR x16, .+8
static const uint32_t kLdrX16Plus8 = 0x58000050U;
#endif // defined(_M_ARM64)
public:
template <typename... Args>
explicit WindowsDllDetourPatcher(Args... aArgs)
: WindowsDllPatcherBase<VMPolicy>(std::forward<Args>(aArgs)...),
mFlags(DetourFlags::eDefault) {}
: WindowsDllPatcherBase<VMPolicy>(std::forward<Args>(aArgs)...) {}
~WindowsDllDetourPatcher() { Clear(); }
@ -64,7 +73,7 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
#elif defined(_M_X64)
size_t nBytes = 2 + sizeof(intptr_t);
#elif defined(_M_ARM64)
size_t nBytes = 4;
size_t nBytes = 2 * sizeof(uint32_t) + sizeof(uintptr_t);
#else
# error "Unknown processor type"
#endif
@ -148,32 +157,25 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
#elif defined(_M_ARM64)
// Ensure that we see the instructions that we expect
// Ensure that we see the instruction that we expect
Maybe<uint32_t> inst1 = origBytes.ReadLong();
if (!inst1) {
continue;
}
if (inst1.value() != 0x58000050) {
if (inst1.value() == kLdrX16Plus8) {
if (!Clear16BytePatch(origBytes, tramp.GetCurrentRemoteAddress())) {
continue;
}
} else if (arm64::IsUnconditionalBranchImm(inst1.value())) {
if (!Clear4BytePatch(inst1.value(), origBytes)) {
continue;
}
} else {
MOZ_ASSERT_UNREACHABLE("Unrecognized patch!");
continue;
}
Maybe<uint32_t> inst2 = origBytes.ReadLong();
if (!inst2) {
continue;
}
if (inst2.value() != arm64::BuildUnconditionalBranchToRegister(16)) {
MOZ_ASSERT_UNREACHABLE("Unrecognized patch!");
continue;
}
// Clobber the pointer to our hook function with a pointer to the
// start of the trampoline.
origBytes.WritePointer(tramp.GetCurrentRemoteAddress());
origBytes.Commit();
#else
# error "Unknown processor type"
#endif
@ -272,37 +274,109 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
}
#endif // defined(_M_X64)
void Init(DetourFlags aFlags = DetourFlags::eDefault, int aNumHooks = 0) {
#if defined(_M_ARM64)
bool Clear4BytePatch(const uint32_t aBranchImm,
WritableTargetFunction<MMPolicyT>& aOrigBytes) {
MOZ_ASSERT(arm64::IsUnconditionalBranchImm(aBranchImm));
arm64::LoadOrBranch decoded = arm64::BUncondImmDecode(
aOrigBytes.GetCurrentAddress() - sizeof(uint32_t), aBranchImm);
uintptr_t trampPtr = decoded.mAbsAddress;
// trampPtr points to an intermediate trampoline that contains a veneer.
// We back up by sizeof(uintptr_t) so that we can access the pointer to the
// stub trampoline.
// We want trampLen to be the size of the veneer, plus one pointer (since
// we are backing up trampPtr by one pointer)
size_t trampLen = 16 + sizeof(uintptr_t);
WritableTargetFunction<MMPolicyT> writableIntermediate(
this->mVMPolicy, trampPtr - sizeof(uintptr_t), trampLen);
if (!writableIntermediate) {
return false;
}
Maybe<uintptr_t> stubTramp = writableIntermediate.ReadEncodedPtr();
if (!stubTramp || !stubTramp.value()) {
return false;
}
Maybe<uint32_t> inst1 = writableIntermediate.ReadLong();
if (!inst1 || inst1.value() != kLdrX16Plus8) {
return false;
}
return Clear16BytePatch(writableIntermediate, stubTramp.value());
}
bool Clear16BytePatch(WritableTargetFunction<MMPolicyT>& aOrigBytes,
const uintptr_t aResetToAddress) {
Maybe<uint32_t> inst2 = aOrigBytes.ReadLong();
if (!inst2) {
return false;
}
if (inst2.value() != arm64::BuildUnconditionalBranchToRegister(16)) {
MOZ_ASSERT_UNREACHABLE("Unrecognized patch!");
return false;
}
// Clobber the pointer to our hook function with a pointer to the
// start of the trampoline.
aOrigBytes.WritePointer(aResetToAddress);
aOrigBytes.Commit();
return true;
}
#endif // defined(_M_ARM64)
void Init(DetourFlags aFlags = DetourFlags::eDefault) {
if (Initialized()) {
return;
}
mFlags = aFlags;
if (aNumHooks == 0) {
// Win32 allocates VM addresses at a 64KiB granularity, so by default we
// might as well utilize that entire 64KiB reservation instead of
// artifically constraining ourselves to the page size.
aNumHooks = this->mVMPolicy.GetAllocGranularity() / kHookSize;
}
ReservationFlags resFlags = ReservationFlags::eDefault;
#if defined(_M_X64)
if (aFlags & DetourFlags::eEnable10BytePatch) {
resFlags |= ReservationFlags::eForceFirst2GB;
if (aFlags & DetourFlags::eTestOnlyForceShortPatch) {
aFlags |= DetourFlags::eEnable10BytePatch;
}
#endif // defined(_M_X64)
this->mVMPolicy.Reserve(aNumHooks, resFlags);
mFlags = Some(aFlags);
}
bool Initialized() const { return !!this->mVMPolicy; }
bool Initialized() const { return mFlags.isSome(); }
bool AddHook(FARPROC aTargetFn, intptr_t aHookDest, void** aOrigFunc) {
ReadOnlyTargetFunction<MMPolicyT> target(
this->ResolveRedirectedAddress(aTargetFn));
CreateTrampoline(target, aHookDest, aOrigFunc);
TrampPoolT* trampPool = nullptr;
#if defined(_M_ARM64)
// ARM64 uses two passes to build its trampoline. The first pass uses a
// null tramp to determine how many bytes are needed. Once that is known,
// CreateTrampoline calls itself recursively with a "real" tramp.
Trampoline<MMPolicyT> tramp(nullptr);
#else
Maybe<TrampPoolT> maybeTrampPool = DoReserve();
MOZ_ASSERT(maybeTrampPool);
if (!maybeTrampPool) {
return false;
}
trampPool = maybeTrampPool.ptr();
Maybe<Trampoline<MMPolicyT>> maybeTramp(trampPool->GetNextTrampoline());
if (!maybeTramp) {
return false;
}
Trampoline<MMPolicyT> tramp(std::move(maybeTramp.ref()));
#endif
CreateTrampoline(target, trampPool, tramp, aHookDest, aOrigFunc);
if (!*aOrigFunc) {
return false;
}
@ -310,9 +384,75 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
return true;
}
protected:
const static int kHookSize = 128;
private:
/**
* This function returns a maximum distance that can be reached by a single
* unconditional jump instruction. This is dependent on the processor ISA.
* Note that this distance is *exclusive* when added to the pivot, so the
* distance returned by this function is actually
* (maximum_absolute_offset + 1).
*/
static uint32_t GetDefaultPivotDistance() {
#if defined(_M_ARM64)
// Immediate unconditional branch allows for +/- 128MB
return 0x08000000U;
#elif defined(_M_IX86) || defined(_M_X64)
// For these ISAs, our distance will assume the use of an unconditional jmp
// with a 32-bit signed displacement.
return 0x80000000U;
#else
# error "Not defined for this processor arch"
#endif
}
/**
* If we're reserving trampoline space for a specific module, we base the
* pivot off of the median address of the module's .text section. While this
* may not be precise, it should be accurate enough for our purposes: To
* ensure that the trampoline space is reachable by any executable code in the
* module.
*/
Maybe<TrampPoolT> ReserveForModule(HMODULE aModule) {
nt::PEHeaders moduleHeaders(aModule);
if (!moduleHeaders) {
return Nothing();
}
Maybe<Span<const uint8_t>> textSectionInfo =
moduleHeaders.GetTextSectionInfo();
if (!textSectionInfo) {
return Nothing();
}
const uint8_t* median = textSectionInfo.value().data() +
(textSectionInfo.value().LengthBytes() / 2);
return this->mVMPolicy.Reserve(reinterpret_cast<uintptr_t>(median),
GetDefaultPivotDistance());
}
Maybe<TrampPoolT> DoReserve(HMODULE aModule = nullptr) {
if (aModule) {
return ReserveForModule(aModule);
}
uintptr_t pivot = 0;
uint32_t distance = 0;
#if defined(_M_X64)
if (mFlags.value() & DetourFlags::eEnable10BytePatch) {
// We must stay below the 2GB mark because a 10-byte patch uses movsxd
// (ie, sign extension) to expand the pointer to 64-bits, so bit 31 of any
// pointers into the reserved region must be 0.
pivot = 0x40000000U;
distance = 0x40000000U;
}
#endif // defined(_M_X64)
return this->mVMPolicy.Reserve(pivot, distance);
}
protected:
#if !defined(_M_ARM64)
const static int kPageSize = 4096;
@ -563,13 +703,11 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
}
void CreateTrampoline(ReadOnlyTargetFunction<MMPolicyT>& origBytes,
TrampPoolT* aTrampPool, Trampoline<MMPolicyT>& aTramp,
intptr_t aDest, void** aOutTramp) {
*aOutTramp = nullptr;
Trampoline<MMPolicyT> tramp(this->mVMPolicy.GetNextTrampoline());
if (!tramp) {
return;
}
Trampoline<MMPolicyT>& tramp = aTramp;
// The beginning of the trampoline contains two pointer-width slots:
// [0]: |this|, so that we know whether the trampoline belongs to us;
@ -713,8 +851,9 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
// not set to true unless we detect that a 10-byte patch is necessary.
// OTOH, for testing purposes, if we want to force a 10-byte patch, we
// always initialize |use10BytePatch| to |true|.
bool use10BytePatch = (mFlags & DetourFlags::eTestOnlyForce10BytePatch) ==
DetourFlags::eTestOnlyForce10BytePatch;
bool use10BytePatch =
(mFlags.value() & DetourFlags::eTestOnlyForceShortPatch) ==
DetourFlags::eTestOnlyForceShortPatch;
const uint32_t bytesRequired = use10BytePatch ? 10 : 13;
while (origBytes.GetOffset() < bytesRequired) {
@ -734,7 +873,7 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
// If our trampoline space is located in the lowest 2GB, we can do a ten
// byte patch instead of a thirteen byte patch.
if (this->mVMPolicy.IsTrampolineSpaceInLowest2GB() &&
if (aTrampPool && aTrampPool->IsInLowest2GB() &&
origBytes.GetOffset() >= 10) {
use10BytePatch = true;
break;
@ -1153,17 +1292,22 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
}
#elif defined(_M_ARM64)
// We could probably decrease this value under certain scenarios, depending
// on the proximity of the hook function to the target function, but this
// would add additional complexity that we don't have time to deal with at
// the moment.
// The number of bytes required to facilitate a detour depends on the
// proximity of the hook function to the target function. In the best case,
// we can branch within +/- 128MB of the current location, requiring only
// 4 bytes. In the worst case, we need 16 bytes to load an absolute address
// into a register and then branch to it.
const uint32_t kWorstCaseBytesRequired = 16;
const uint32_t bytesRequiredFromDecode =
(mFlags.value() & DetourFlags::eTestOnlyForceShortPatch)
? 4
: kWorstCaseBytesRequired;
while (origBytes.GetOffset() < kWorstCaseBytesRequired) {
while (origBytes.GetOffset() < bytesRequiredFromDecode) {
uintptr_t curPC = origBytes.GetCurrentAbsolute();
uint32_t curInst = origBytes.ReadNextInstruction();
Result<arm64::LoadInfo, arm64::PCRelCheckError> pcRelInfo =
Result<arm64::LoadOrBranch, arm64::PCRelCheckError> pcRelInfo =
arm64::CheckForPCRel(curPC, curInst);
if (pcRelInfo.isErr()) {
if (pcRelInfo.unwrapErr() ==
@ -1173,8 +1317,16 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
continue;
}
// No decoder available for PC-relative instruction; fail.
return;
// At this point we have determined that there is no decoder available
// for the current, PC-relative, instruction.
// origBytes is now pointing one instruction past the one that we
// need the trampoline to jump back to.
if (!origBytes.BackUpOneInstruction()) {
return;
}
break;
}
// We need to load an absolute address into a particular register
@ -1202,7 +1354,7 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
tramp.WriteDisp32(origBytes.GetAddress());
}
#elif defined(_M_X64)
// If the we found a Jmp, we don't need to add another instruction. However,
// If we found a Jmp, we don't need to add another instruction. However,
// if we found a _conditional_ jump or a CALL (or no control operations
// at all) then we still need to run the rest of aOriginalFunction.
if (!foundJmp) {
@ -1211,11 +1363,50 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
}
}
#elif defined(_M_ARM64)
// Let's find out how many bytes we have available to us for patching
uint32_t numBytesForPatching = tramp.GetCurrentExecutableCodeLen();
if (!numBytesForPatching) {
// There's nothing we can do
return;
}
if (tramp.IsNull()) {
// Recursive case
HMODULE targetModule = nullptr;
if (numBytesForPatching < kWorstCaseBytesRequired) {
if (!::GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(origBytes.GetBaseAddress()),
&targetModule)) {
return;
}
}
Maybe<TrampPoolT> maybeTrampPool = DoReserve(targetModule);
MOZ_ASSERT(maybeTrampPool);
if (!maybeTrampPool) {
return;
}
Maybe<Trampoline<MMPolicyT>> maybeRealTramp(
maybeTrampPool.ref().GetNextTrampoline());
if (!maybeRealTramp) {
return;
}
origBytes.Rewind();
CreateTrampoline(origBytes, maybeTrampPool.ptr(), maybeRealTramp.ref(),
aDest, aOutTramp);
return;
}
// Write the branch from the trampoline back to the original code
tramp.WriteLoadLiteral(origBytes.GetAddress(), 16);
tramp.WriteInstruction(arm64::BuildUnconditionalBranchToRegister(16));
#else
# error "Unsupported processor architecture"
#endif
@ -1241,11 +1432,14 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
// Note: Even if the target function is also below 2GB, we still use an
// intermediary trampoline so that we consistently have a 64-bit pointer
// that we can use to reset the trampoline upon interceptor shutdown.
Trampoline<MMPolicyT> callTramp(this->mVMPolicy.GetNextTrampoline());
if (!callTramp) {
Maybe<Trampoline<MMPolicyT>> maybeCallTramp(
aTrampPool->GetNextTrampoline());
if (!maybeCallTramp) {
return;
}
Trampoline<MMPolicyT> callTramp(std::move(maybeCallTramp.ref()));
// Write a null instance so that Clear() does not consider this tramp to
// be a normal tramp to be torn down.
callTramp.WriteEncodedPointer(nullptr);
@ -1296,11 +1490,35 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
#elif defined(_M_ARM64)
// Now patch the original function
// LDR x16, .+8
target.WriteLong(0x58000050);
// BR x16
target.WriteLong(arm64::BuildUnconditionalBranchToRegister(16));
target.WritePointer(aDest);
if (numBytesForPatching < kWorstCaseBytesRequired) {
// Let's try a 4 byte patch
MOZ_ASSERT(aTrampPool);
if (!aTrampPool) {
return;
}
uintptr_t hookDest = arm64::MakeVeneer(*aTrampPool, trampPtr, aDest);
if (!hookDest) {
return;
}
Maybe<uint32_t> branchImm = arm64::BuildUnconditionalBranchImm(
target.GetCurrentAddress(), hookDest);
if (!branchImm) {
return;
}
target.WriteLong(branchImm.value());
} else {
// The default patch requires 16 bytes
// LDR x16, .+8
target.WriteLong(kLdrX16Plus8);
// BR x16
target.WriteLong(arm64::BuildUnconditionalBranchToRegister(16));
target.WritePointer(aDest);
}
#else
# error "Unsupported processor architecture"

Просмотреть файл

@ -696,6 +696,15 @@ class MOZ_STACK_CLASS ReadOnlyTargetFunction final {
return instruction;
}
bool BackUpOneInstruction() {
if (mOffset < sizeof(uint32_t)) {
return false;
}
mOffset -= sizeof(uint32_t);
return true;
}
#else
uint8_t const& operator*() const {
@ -730,6 +739,8 @@ class MOZ_STACK_CLASS ReadOnlyTargetFunction final {
#endif
void Rewind() { mOffset = 0; }
uint32_t GetOffset() const { return mOffset; }
uintptr_t OffsetToAbsolute(const uint8_t aOffset) const {
@ -761,7 +772,7 @@ class MOZ_STACK_CLASS ReadOnlyTargetFunction final {
mTargetBytes->GetBase() + aOffset,
effectiveLength);
return std::move(result);
return result;
}
private:

Просмотреть файл

@ -260,8 +260,6 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS FuncHookCrossProcess final {
FuncPtrT mOrigFunc;
};
enum { kDefaultTrampolineSize = 128 };
template <typename MMPolicyT, typename InterceptorT>
struct TypeResolver;
@ -278,7 +276,7 @@ struct TypeResolver<mozilla::interceptor::MMPolicyOutOfProcess, InterceptorT> {
};
template <typename VMPolicy = mozilla::interceptor::VMSharingPolicyShared<
mozilla::interceptor::MMPolicyInProcess, kDefaultTrampolineSize>>
mozilla::interceptor::MMPolicyInProcess, true>>
class WindowsDllInterceptor final
: public TypeResolver<typename VMPolicy::MMPolicyT,
WindowsDllInterceptor<VMPolicy>> {
@ -291,7 +289,6 @@ class WindowsDllInterceptor final
#endif // defined(_M_IX86)
HMODULE mModule;
int mNHooks;
public:
template <typename... Args>
@ -302,8 +299,7 @@ class WindowsDllInterceptor final
mNopSpacePatcher(std::forward<Args>(aArgs)...)
#endif // defined(_M_IX86)
,
mModule(nullptr),
mNHooks(0) {
mModule(nullptr) {
}
WindowsDllInterceptor(const WindowsDllInterceptor&) = delete;
@ -314,7 +310,7 @@ class WindowsDllInterceptor final
~WindowsDllInterceptor() { Clear(); }
template <size_t N>
void Init(const char (&aModuleName)[N], int aNumHooks = 0) {
void Init(const char (&aModuleName)[N]) {
wchar_t moduleName[N];
for (size_t i = 0; i < N; ++i) {
@ -323,27 +319,22 @@ class WindowsDllInterceptor final
moduleName[i] = aModuleName[i];
}
Init(moduleName, aNumHooks);
Init(moduleName);
}
void Init(const wchar_t* aModuleName, int aNumHooks = 0) {
void Init(const wchar_t* aModuleName) {
if (mModule) {
return;
}
mModule = ::LoadLibraryW(aModuleName);
mNHooks = aNumHooks;
}
/** Force a specific configuration for testing purposes. NOT to be used in
production code! **/
void TestOnlyDetourInit(const wchar_t* aModuleName, DetourFlags aFlags,
int aNumHooks = 0) {
Init(aModuleName, aNumHooks);
if (!mDetourPatcher.Initialized()) {
mDetourPatcher.Init(aFlags, mNHooks);
}
void TestOnlyDetourInit(const wchar_t* aModuleName, DetourFlags aFlags) {
Init(aModuleName);
mDetourPatcher.Init(aFlags);
}
void Clear() {
@ -429,7 +420,7 @@ class WindowsDllInterceptor final
}
#endif // defined(_M_X64)
mDetourPatcher.Init(flags, mNHooks);
mDetourPatcher.Init(flags);
}
return mDetourPatcher.AddHook(aProc, aHookDest, aOrigFunc);
@ -449,8 +440,7 @@ using WindowsDllInterceptor = interceptor::WindowsDllInterceptor<>;
using CrossProcessDllInterceptor = interceptor::WindowsDllInterceptor<
mozilla::interceptor::VMSharingPolicyUnique<
mozilla::interceptor::MMPolicyOutOfProcess,
mozilla::interceptor::kDefaultTrampolineSize>>;
mozilla::interceptor::MMPolicyOutOfProcess>>;
} // namespace mozilla