Map AArch64 xsp to Unicorn's SP register - #361
Open
plowsec wants to merge 1 commit into
Open
Conversation
ArchAArch64 had no stack-pointer entry in uc_regs or vex_to_unicorn_map. The mapping is built from canonical register names only, and AArch64's canonical name is `xsp`, for which Unicorn has no constant, it has UC_ARM64_REG_SP. The register was therefore skipped, and AArch64 blocks handed to Unicorn ran with a stack pointer of zero.
twizmwazin
requested changes
Aug 3, 2026
| class ArchAArch64(Arch): | ||
| def __init__(self, endness=Endness.LE): | ||
| super().__init__(endness) | ||
| if _unicorn is not None: |
Member
There was a problem hiding this comment.
Does this seem like the best place to have put this?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AArch64's stack pointer is missing from the Unicorn register map, so Unicorn silently never runs
Problem
archinfo publishes the table that says which angr register corresponds to which Unicorn register. On AArch64 that table has no entry for the stack pointer. This can be confirmed like this:
Nothing warns about this. The consequence is that whenever angr hands an AArch64 block to the Unicorn CPU emulator, the emulator starts with a stack pointer of zero, faults on the first stack access, and angr quietly falls back to interpreting the blockand then disables Unicorn for a while. Running a stack round trip (
sub sp, sp, #16/str/ldr/add sp, sp, #16) fromsp = 0x70000000withangr.options.UNICORNenabled, at INFO logging:The
mmap [0xfffffffffffff000, …]line demonstrates it: the emulator computed0 - 16and tried to write there, so it really did start withsp = 0.STOP_ERROR after 0 stepsis angr throwing the attempt away,IRSBis the interpreter that actually produced the answer, andenabling cooldownmeans Unicorn is skipped for subsequent blocks too.With the patch, the same run:
The emulator now maps the real stack at
0x70000000and completes the block.Note that the final register values are the same either way, because falling back to the interpreter is correct. What is lost is Unicorn itself: on AArch64 it never does any useful work, silently, no matter what the user asks for. That is the defect being reported here. Whether a zero stack pointer can also produce a wrong result rather than a clean fault depends on whether the faulting address happens to be mapped, but I haven't investigate that.
root cause
Arch._setup_unicorn_mappingsbuilds the map by walking canonical register names(
arch.py:296-301):register_namesmaps a VEX offset to the register's canonical name only; alias names are never consulted. AArch64 names its stack pointerxsp, withspmerely an alias (arch_aarch64.py:124-131), and Unicorn has noUC_ARM64_REG_XSPonlyUC_ARM64_REG_SP. The lookup misses, the register is skipped, and the derivedvex_to_unicorn_map(arch.py:303-311) is missing VEX offset 264 as well. Every other architecture escapes this by coincidence of naming: 32-bit ARM calls itspand x86-64 calls itrsp, and both of those names exist in Unicorn.suggested fix
Add the one missing entry, mapping AArch64's
xsptoUC_ARM64_REG_SP.Verified against Unicorn 2.1.4:
SPis the 64-bit architectural stack pointer and the correct counterpart of VEX'sXSP.WSPis its 32-bit view; mapping to that would silently truncate every stack pointer transferred in or out, which is the mistake worth being explicit about avoiding.reproducer
repro.zip
It builds
repro.S, executes one block with Unicorn enabled from a known stack pointer, and checks which engine ran and what came back. It also installs angr's AArch64 Unicorn hook itself, so that a separate angr-side bug (will submit separately) does not hide this one. Before the patch the engine reported isIRSB. After it,Unicorn (STOP_STOPPOINT after 1 steps), withspandx0surviving.note
Doing this end to end also needs angr's Unicorn hook dispatch to accept AArch64, which currently raises
SimUnicornUnsupport. I will open that separately against angr if I have the time.