Files
vphone-cli/scripts/patchers/kernel_jb_patch_port_to_map.py
Lakr 5655fd78d7 Fix B8 convert_port_to_map: was patching PAC check instead of kernel_map guard
Root cause: backward branch search found PAC validation B.EQ (target fell
within error region range) instead of the kernel_map guard B.NE. New approach
walks backward from panic string ADRP to find CMP+B.cond where the branch
goes forward past the panic, then makes it unconditional. Also enables B6
and B8 in find_all(). Boot-tested: PASS.
2026-03-04 20:54:16 +08:00

72 lines
2.9 KiB
Python

"""Mixin: KernelJBPatchPortToMapMixin."""
from .kernel_jb_base import ARM64_OP_IMM, NOP, struct, _rd32
class KernelJBPatchPortToMapMixin:
def patch_convert_port_to_map(self):
"""Skip panic in _convert_port_to_map_with_flavor.
Anchor: 'userspace has control access to a kernel map' panic string.
The function flow around the kernel_map check is:
CMP X16, X8 ; compare map ptr with kernel_map
B.NE normal_path ; if NOT kernel_map, continue normally
; fall through: set up panic args and call _panic (noreturn)
Fix: walk backward from the string ref to find the B.cond that
guards the panic fall-through, then make it unconditional.
This causes the kernel_map case to take the normal path instead
of panicking, allowing userspace to access the kernel map.
"""
self._log("\n[JB] _convert_port_to_map_with_flavor: skip panic")
str_off = self.find_string(b"userspace has control access to a kernel map")
if str_off < 0:
self._log(" [-] panic string not found")
return False
refs = self.find_string_refs(str_off, *self.kern_text)
if not refs:
self._log(" [-] no code refs")
return False
for adrp_off, add_off, _ in refs:
# Walk backward from the string ADRP to find CMP + B.cond
# The pattern is: CMP Xn, Xm; B.NE target
# We want to change B.NE to unconditional B (always skip panic).
for back in range(adrp_off - 4, max(adrp_off - 0x60, 0), -4):
d = self._disas_at(back, 2)
if not d or len(d) < 2:
continue
i0, i1 = d[0], d[1]
# Look for CMP + B.NE/B.CS/B.HI (conditional branch away from
# the panic path). The branch target should be AFTER the panic
# call (i.e., forward past the string ref region).
if i0.mnemonic != "cmp":
continue
if not i1.mnemonic.startswith("b."):
continue
# Decode the branch target
target, kind = self._decode_branch_target(back + 4)
if target is None:
continue
# The branch should go FORWARD past the panic (beyond adrp_off)
if target <= adrp_off:
continue
# Found the conditional branch that skips the panic path.
# Replace it with unconditional B to same target.
b_bytes = self._encode_b(back + 4, target)
if b_bytes:
self.emit(
back + 4,
b_bytes,
f"b 0x{target:X} "
f"[_convert_port_to_map skip panic]",
)
return True
self._log(" [-] branch site not found")
return False