From 0b558603ab416c58922402b28e0b29036b4b6fb7 Mon Sep 17 00:00:00 2001 From: r0ny123 <49360849+r0ny123@users.noreply.github.com> Date: Fri, 11 Sep 2026 00:19:48 +0530 Subject: [PATCH] test(tests): pin the bti j split that no bundled binary can show #310 refuses a `bti j` word as a function start and keeps `bti c`, on the grounds that a jump-only hint marks an indirect branch target inside a routine where a call hint marks a callable entry. #322 records the figure behind it as unverifiable here, because no bundled fixture contained a `bti j` word and every AArch64 fixture was bit-identical across the change. The first half of that has since stopped being true: #300 bundled elf_cxx_landing_pads_arm64_xored, which carries five `bti j` words, and they do reach the rule. The second half still holds, and now for a reason worth writing down. All five sit inside exception landing pads, where the shape test refuses them on its own account and the LSDA rule refuses them earlier still, so the flag decides nothing on that fixture and toggling it moves no address. What the flag needs in order to decide by itself is a `bti j` the shape test would otherwise accept: after alignment padding, opening a block that looks like an entry, in an image declaring no landing pads at all. A raw buffer is exactly that image, so this builds one rather than asking for a binary that cannot be bundled. With the flag off the hint word is booked as a function and the block behind it is not; with it on the hint is refused and the block is recovered in its place. `bti c`, `bti jc` and a bare `bti` are asserted unaffected in both settings, which is what makes the case measure the jump-only split rather than bti handling in general. --- tests/testAArch64BtiTargetType.py | 99 +++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/testAArch64BtiTargetType.py diff --git a/tests/testAArch64BtiTargetType.py b/tests/testAArch64BtiTargetType.py new file mode 100644 index 00000000..e74ded98 --- /dev/null +++ b/tests/testAArch64BtiTargetType.py @@ -0,0 +1,99 @@ +#!/usr/bin/python +"""`bti j` marks a jump target, not a callable entry. + +`USE_AARCH64_BTI_TARGET_TYPE` refuses a `bti j` word as a function start: the hint says an +indirect *branch* may land there, which a compiler emits for jump-table cases and cleanup +blocks inside a routine, where `bti c` marks a call target and stays a plausible entry. + +Every AArch64 binary bundled with this repo carries its `bti j` words inside exception +landing pads, where the shape test refuses them on its own account and the LSDA rule refuses +them before that -- so on those the flag is redundant and changes nothing either way. The +image below places the word where the shape test would otherwise accept it: after alignment +padding, opening a block that looks exactly like an entry, in a raw buffer that declares no +`.eh_frame` and so no landing pads at all. That is the one arrangement in which the flag +decides the outcome by itself. +""" + +import struct +import unittest + +from smda.Disassembler import Disassembler +from smda.SmdaConfig import SmdaConfig + +BASE = 0x400000 + +NOP = 0xD503201F +RET = 0xD65F03C0 +#: stp x29, x30, [sp, #-16]! / ldp x29, x30, [sp], #16 -- an ordinary frame open and close +STP_FRAME = 0xA9BF7BFD +LDP_FRAME = 0xA8C17BFD + +BTI = 0xD503241F +BTI_C = 0xD503245F +BTI_J = 0xD503249F +BTI_JC = 0xD50324DF + +#: where the hint word lands, and where the block behind it starts +HINT = BASE + 0x20 +BODY = HINT + 4 + + +def words(*values): + return b"".join(struct.pack("