Skip to content

Commit 2a75c68

Browse files
committed
Support BTI enabled devices [Pixel 10 Pro XL]
During the entry point hijack phase on ARMv9 hardware with Branch Target Identification (BTI) enabled, dispatching remote calls to bionic functions (such as `dlopen`) resulted in an immediate Branch Target Exception (SIGILL). This crash is caused by a stale Branch Type (`BTYPE`) state. When the Android dynamic linker transfers control to the target's `AT_ENTRY`, it uses an indirect branch instruction (`BR`). This hardware action updates the CPU's `PSTATE` register, setting the `BTYPE` field to `0b11` (Indirect Jump). When the tracer pauses the process and redirects execution to `dlopen`, the CPU inherits this `PSTATE`. Modern Android bionic libraries are compiled with BTI and start with `PACIASP` or `BTI c` landing pads. These landing pads strictly require the incoming `BTYPE` to be `0b01` (Direct Call) or `0b10` (Indirect Call). Confronted with `0b11`, the CPU assumes a JOP (Jump-Oriented Programming) exploit and raises a SIGILL. This commit resolves the issue by clearing the `BTYPE` field (bits 10 and 11) in the `pstate` register structure prior to initiating any remote calls. Resetting the field to `0b00` prevents the hardware from enforcing BTI validation on the immediate next instruction. The original `PSTATE` is preserved in the register backup, ensuring that the target executable can securely validate its own BTI landing pad when execution is fully restored. References: https://developer.android.com/ndk/guides/abis#armv9_enabling_pac_and_bti_for_cc
1 parent de38c62 commit 2a75c68

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

loader/src/ptracer/ptracer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,18 @@ bool inject_on_main(int pid, const char *lib_path) {
184184
}
185185

186186
// Backup the current registers before we start making remote calls.
187+
// It is vital we keep the original PSTATE intact in the backup, so
188+
// the original executable can securely validate its own BTI pad upon final resume.
187189
memcpy(&backup, &regs, sizeof(regs));
190+
191+
#if defined(__aarch64__)
192+
// Clear the BTYPE field (bits 10 and 11) in PSTATE.
193+
// The previous indirect branch from the linker set BTYPE to 0b11.
194+
// If we jump into BTI-protected bionic libraries (like libdl.so) with BTYPE=0b11,
195+
// the CPU will throw a Branch Target Exception (SIGILL).
196+
regs.pstate &= ~(3ULL << 10);
197+
#endif
198+
188199
map = MapInfo::Scan(std::to_string(pid)); // Re-scan maps as they may have changed.
189200
auto local_map = MapInfo::Scan();
190201
auto libc_return_addr = find_module_return_addr(map, "libc.so");

0 commit comments

Comments
 (0)