tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f1f12824347f9714238f973e009945e1b0628b42
parent e378f44562245e675730580d935069112efe6864
Author: Greg Stoll <gstoll@mozilla.com>
Date:   Thu, 20 Nov 2025 16:30:29 +0000

Bug 1997530 - skip test that requires detouring a B instruction on Windows AArch64 r=yjuglaret,win-reviewers

TrackPopupMenu() on ARM uses a B instruction, which is PC-relative. We
don't actually need to detour this, so I think it's OK to just skip this
test on ARM64.

I originally went down the path of making us support B instructions, since
they don't set a register as a side effect. However, this is harder than
I thought as explained in the PatcherDetour.h code - since we use a two-pass
system, on the first pass we assume the remote offset is 0, and that may mean
the offset is out of the 26-bit range in the B instruction.

Since I had already done the work of splitting up the BL and B instructions
in gPCRelTests I went ahead and kept those so it will be (slightly) easier
to implement this in the future if we need to.

While doing this work, I noticed that the already-existing BUncondImmDecode
was incorrect; per the ARM64 documentation the offset to jump to is the
26 bits in the instruction multipled by 4. ( https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- )
We were only using this in Clear4BytePatch ( https://searchfox.org/firefox-main/rev/1a8c62b86277005f907151bc5389cf5c5091e76f/toolkit/xre/dllservices/mozglue/interceptor/PatcherDetour.h#373-374 ), so I'm guessing this didn't come up much.

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

Diffstat:
Mtoolkit/xre/dllservices/mozglue/interceptor/Arm64.cpp | 11++++++-----
Mtoolkit/xre/dllservices/mozglue/interceptor/PatcherDetour.h | 22++++++++++++++++++++--
Mtoolkit/xre/dllservices/tests/TestArm64Disassembler.cpp | 27+++++++++++++++++++++++++++
Mtoolkit/xre/dllservices/tests/TestDllInterceptor.cpp | 3+++
4 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/toolkit/xre/dllservices/mozglue/interceptor/Arm64.cpp b/toolkit/xre/dllservices/mozglue/interceptor/Arm64.cpp @@ -43,7 +43,7 @@ static LoadOrBranch ADRPDecode(const uintptr_t aPC, const uint32_t aInst) { MFBT_API LoadOrBranch BUncondImmDecode(const uintptr_t aPC, const uint32_t aInst) { - int32_t offset = SignExtend<int32_t>(aInst & 0x03FFFFFFU, 26); + intptr_t offset = SignExtend<intptr_t>((aInst & 0x03FFFFFFU) << 2, 28); return LoadOrBranch(aPC + offset); } @@ -55,10 +55,11 @@ static const PCRelativeLoadTest gPCRelTests[] = { {0x9F000000, 0x90000000, &ADRPDecode}, // ADRP {0xFF000000, 0x58000000, nullptr}, // LDR (literal) 64-bit GPR {0x3B000000, 0x18000000, nullptr}, // LDR (literal) (remaining forms) - {0x7C000000, 0x14000000, nullptr}, // B (unconditional immediate) - {0xFE000000, 0x54000000, nullptr}, // B.Cond - {0x7E000000, 0x34000000, nullptr}, // Compare and branch (imm) - {0x7E000000, 0x36000000, nullptr}, // Test and branch (imm) + {0xFC000000, 0x94000000, nullptr}, // BL (unconditional immediate) + {0xFC000000, 0x14000000, &BUncondImmDecode}, // B (unconditional immediate) + {0xFE000000, 0x54000000, nullptr}, // B.Cond + {0x7E000000, 0x34000000, nullptr}, // Compare and branch (imm) + {0x7E000000, 0x36000000, nullptr}, // Test and branch (imm) }; /** diff --git a/toolkit/xre/dllservices/mozglue/interceptor/PatcherDetour.h b/toolkit/xre/dllservices/mozglue/interceptor/PatcherDetour.h @@ -937,6 +937,8 @@ class WindowsDllDetourPatcher final } #endif // defined(_M_X64) + // Note that *aOutTramp is not set until CreateTrampoline has completed + // successfully, so callers can use that to check for success. void CreateTrampoline(ReadOnlyTargetFunction<MMPolicyT>& origBytes, TrampPoolT* aTrampPool, Trampoline<MMPolicyT>& aTramp, intptr_t aDest, void** aOutTramp) { @@ -964,8 +966,7 @@ class WindowsDllDetourPatcher final auto clearInstanceOnFailure = MakeScopeExit([this, aOutTramp, &tramp, &origBytes]() -> void { - // *aOutTramp is not set until CreateTrampoline has completed - // successfully, so we can use that to check for success. + // If *aOutTramp was set this method ran successfully. if (*aOutTramp) { return; } @@ -1699,6 +1700,23 @@ class WindowsDllDetourPatcher final break; } + if (pcRelInfo.inspect().mType == arm64::LoadOrBranch::Type::Branch) { + // We need to branch to the original target of this instruction. + // This is a problem because on the first pass + // GetCurrentRemoteAddress() is 0, so the offset may be too large for + // BuildUnconditionalBranchImm. We don't need this for now on ARM64. + // so just behave like we don't have a decoder. + + // origBytes is now pointing one instruction past the one that we + // need the trampoline to jump back to. + if (!origBytes.BackUpOneInstruction()) { + return; + } + + break; + } + + MOZ_ASSERT(pcRelInfo.inspect().mType == arm64::LoadOrBranch::Type::Load); // We need to load an absolute address into a particular register tramp.WriteLoadLiteral(pcRelInfo.inspect().mAbsAddress, pcRelInfo.inspect().mDestReg); diff --git a/toolkit/xre/dllservices/tests/TestArm64Disassembler.cpp b/toolkit/xre/dllservices/tests/TestArm64Disassembler.cpp @@ -139,6 +139,33 @@ bool TestCheckForPCRelBr() { return true; } +bool TestCheckForAbsB() { + // b 0x400 + Result<LoadOrBranch, PCRelCheckError> result = + CheckForPCRel(kExamplePCValue, 0x14000100); + if (result.isErr()) { + auto error = result.unwrapErr(); + TEST_FAILED("Failed to decode b, got PCRelCheckError %d.\n", error); + } + + auto loadOrBranch = result.unwrap(); + if (loadOrBranch.mType != LoadOrBranch::Type::Branch) { + TEST_FAILED("Computed an incorrect LoadOrBranch::Type for b, got %d.\n", + loadOrBranch.mType); + } + if (loadOrBranch.mAbsAddress != (kExamplePCValue + 0x400)) { + TEST_FAILED("Computed a wrong absolute address for b, got address %p.\n", + loadOrBranch.mAbsAddress); + } + if (loadOrBranch.mCond != IntegerConditionCode::AL) { + TEST_FAILED("Computed a wrong condition code for b, got %d.\n", + static_cast<int32_t>(loadOrBranch.mCond)); + } + + TEST_PASS("Properly decoded b.\n"); + return true; +} + int wmain(int argc, wchar_t* argv[]) { if (!TestCheckForPCRelAdrp() || !TestCheckForPCRelAdr() || !TestCheckForPCRelBlr() || !TestCheckForPCRelBr()) { diff --git a/toolkit/xre/dllservices/tests/TestDllInterceptor.cpp b/toolkit/xre/dllservices/tests/TestDllInterceptor.cpp @@ -1547,7 +1547,10 @@ extern "C" int wmain(int argc, wchar_t* argv[]) { TEST_HOOK("user32.dll", GetKeyState, Ignore, 0) && // see Bug 1316415 #endif TEST_HOOK("user32.dll", GetWindowInfo, Equals, FALSE) && +#if defined(_M_IX86) || defined(_M_X64) + // Bug 1997530: TrackPopupMenu uses a PC-relative branch on ARM64. TEST_HOOK("user32.dll", TrackPopupMenu, Equals, FALSE) && +#endif TEST_DETOUR("user32.dll", CreateWindowExW, Equals, nullptr) && TEST_HOOK("user32.dll", InSendMessageEx, Equals, ISMEX_NOSEND) && TEST_HOOK("user32.dll", SendMessageTimeoutW, Equals, 0) &&