Skip to content

Fix SIGILL during remote execution by clearing PSTATE.BTYPE on AArch64#133

Open
JingMatrix wants to merge 2 commits intomasterfrom
arm64
Open

Fix SIGILL during remote execution by clearing PSTATE.BTYPE on AArch64#133
JingMatrix wants to merge 2 commits intomasterfrom
arm64

Conversation

@JingMatrix
Copy link
Copy Markdown
Owner

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

We implement a connection check to bypass process flag retrieval and module execution for isolated processes when the zygisk daemon is unreachable.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] SIGILL crash on injection into zygote on Android 17 Beta 4

1 participant