[AUTOMATED] fix(analysis): arm-literal-pool-string — follow a read-only pool word, so an ARM literal has an owning function - #431
Merged
Conversation
…ly pool word, so an ARM literal has an owning function On ARM the address of a string is not in the instruction that uses it: the constant does not fit an ARM immediate, so the compiler parks it in a literal pool in .text and the code loads it PC-relatively. The reference walk filed the read of the pool word and stopped there, so the literal itself was referenced by nothing and `kuna strings --json` answered `xrefs_count: 0` and no owning function for a string `__libc_start_main` plainly prints. A `read` of a pointer-sized, pointer-aligned location in an allocated NON-writable section now files a second edge, from the same instruction, to the address that word holds. Filing it from the instruction rather than from the pool word is the point: a pool word lies in no function, so the new edge inherits exactly the attribution the existing read edge already had, and no new ownership logic exists to be wrong. Three refusals, each of which would otherwise be a fabricated reference: a narrow read (which is reading a number out of a pool -- and the width has to come from the access, because a LOAD's address varnode is pointer-sized whatever the access is), a writable slot (a GOT entry holds whatever the loader last wrote, not what the image says), and a word that fails the address floor the constant scan already uses. `kuna strings` also reads the function inventory once now: `find_entry_at` rebuilds and linearly scans it per call, which is free while no string has an owner and 0.85 s of a 2.5 s answer once 1,792 of them do. Output is byte-identical. Measured over 15 images: 0 attributions lost, 0 edges added on all 7 x86-64/PE images, 2,239 new string-to-function attributions on u-boot and 763 on the witness -- of which an independent capstone+symtab oracle corroborates 2,234 and 761, with all 14 remaining outliers checked by hand and found to be pool loads the oracle's own sweep missed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mahaloz
added a commit
that referenced
this pull request
Sep 6, 2026
#431 (B_DONE) Round 3 stopped at B_VERIFY, so the three PRs its builders merged never made it back into the backlog: corrupt-elf-section-table, explicit-function-boundary-aborts and arm-literal-pool-string were all still `status: open` with `closing_pr: null` while their fixes were in main and their probes already promoted into tests/cli/. `verify --acceptance-suite --all` on a freshly built 81013ec: 51 needs, 30 pass, 13 fail, 8 indeterminate, 0 regressed — and exactly those three flipped from the FAIL they were filed at. Closed via `needs apply-acceptance`, one PR each. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The problem
On ARM, the address of a string is not in the instruction that uses it — it is in
a literal pool, and the code loads it PC-relatively. kuna files the read of the
pool word and stops there, so the string itself is referenced by nothing and
kuna stringscannot say which function prints it.The pool word plainly holds 0x661bc; nothing joins the two. (Reproduce on any A32
binary:
cstool arm 'b0009fe5'is the load.)The fix
readof a pointer-sized, pointer-aligned location in an allocated,non-writable section files a second edge, from the same instruction, to the
address that word holds — kind
data, the address-taken case.function, so an edge from it would answer "who uses this string?" with another
address instead of a name. The new edge therefore inherits exactly the
attribution the existing
readedge already had.narrow read (reading a number out of a pool), a writable slot (a GOT entry
holds whatever the loader last wrote, not what the image says), and a word that
fails the address floor the constant scan already uses.
LOAD'saddress is pointer-sized whatever the access is, so
ldrh r0,[pool]reads as apointer dereference otherwise.
kuna stringsnow reads the function inventory once.find_entry_atrebuildsand linearly scans it per call, which is free while no string has an owner and
0.85 s of a 2.5 s answer once 1,792 of them do. Output is byte-identical.
The tests
tests/cli/arm-literal-pool-string.json(the promoted acceptance, re-pointed at anew 735-byte vendored A32 fixture — CI has no dataset) plus
kuna-console/tests/verify_poolref.rs, which pins the follow and the threerefusals on that fixture, and unit tests for the dereference and for
data_refs.Before this, the promoted probe reports
functions: [].Sweep of
kuna stringsover 15 images: 0 attributions lost, 0 edges addedon all 7 x86-64/PE images, and on ARM 2,239 new string-to-function attributions on
u-boot and 763 on the witness. An independent capstone+symtab oracle corroborates
2,234 and 761 of those as real PC-relative pool loads; all 14 remaining outliers
were checked by hand and are loads the oracle's own sweep missed, not fabricated
edges. Speed (interleaved, min-of-7): walk +1.1%,
kuna strings+1.6% on u-boot,+3.3% on the witness, −3.8% on an x86-64 control.
Gates:
make test675/675 PARITY OK ·make test-stages635/635 PARITY OK ·make rust-testgreen ·make check-specOK ·make test-cli35/35 ·kuna catalog --checkOK.🤖 Generated with Claude Code