Skip to content

test(tests): bundle an ARM64 PE so the interior-gap rule has a real one - #333

Merged
danielplohmann merged 1 commit into
danielplohmann:masterfrom
r0ny123:test/arm64-pe-interior-gap-fixture
Sep 11, 2026
Merged

test(tests): bundle an ARM64 PE so the interior-gap rule has a real one#333
danielplohmann merged 1 commit into
danielplohmann:masterfrom
r0ny123:test/arm64-pe-interior-gap-fixture

Conversation

@r0ny123

@r0ny123 r0ny123 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Item 2 of #322, the ARM64 PE row — the "bundle fixtures that reach these paths" option rather than the corpus one.

no bundled fixture is an ARM64 PE. Scanning all 104 fixture files for a PE machine field: 8 × i386, 4 × AMD64, 1 × 0xFD1D (the ReadyToRun image), 0 × 0xAA64

Still true, and this closes it. testAArch64PdataExtraction.py drives USE_PE_ARM64_PDATA_INTERIOR_GAPS end to end, but over an image the suite synthesises — which pins the extent decode and the refusal, and cannot say the rule meets the shapes a compiler actually emits.

The fixture

Built from source rather than taken from a system, so it is redistributable and rebuildable:

aarch64-w64-mingw32-clang++ -O2 -g -gdwarf-4 probe.cpp -o probe.exe     # llvm-mingw 20250430

The source deliberately exercises catch funclets, cleanup funclets and a [[noreturn]] cold path — the shapes that get their own unwind record.

It is a control as well as a fixture. clang emits one .pdata record per named function and splits none of them, so the MSVC over-seeding described in item 1 does not occur here. Anything this test refuses is refused on the extent evidence, not on a compiler's chunking habit.

XORed like every other bundled sample. 200K, against 1.1M for the largest already carried and 247K for the next.

What it pins

Eight addresses move between the flag off and on, each interior to a RUNTIME_FUNCTION extent whose own function the analysis recovered:

0x1400014E8  0x1400015F8  0x140001614  0x140001628
0x140001674  0x140001840  0x140001A7C  0x140001B48

They are listed rather than counted, so a change that refuses a different set of eight fails instead of staying green on an unchanged total. on - off is asserted empty as well, so the rule is shown to only refuse.

Three cases: that the fixture really is an 0xAA64 PE (the gap this file exists to close), the refusal itself with both directions asserted, and that none of the eight survives with the rule on. Verified to fail when the rule is disabled at source — two of the three fail, and the machine-field case correctly does not.

Gates

  • python -m pytest tests/2,063 passed, 1 skipped, 2,593 subtests
  • ruff check . and ruff format --check . → clean
  • make typecheck → exit 0, 261 diagnostics, identical to master
  • No src/ change; no existing fixture baseline moves.

What it does not do

It does not make #312's magnitude reproducible in-repo, and does not try to. What the fixtures owe is that the mechanism cannot silently stop working on a real ARM64 PE, which is the durable half of the two options that item lists. The magnitude I measured separately and put on the issue: −10 false positives on ping/robocopy/bcrypt against IDA labels, and −49 with +11 functions gained on six .NET ARM64 images against private-PDB truth.

You marked these as yours in the issue, so this is offered rather than assumed — and bundling a binary is a repo-content call, so close it if you would rather not carry the 200K.

danielplohmann#312's interior-gap rule is driven end to end only over an image the
suite synthesises. That pins the extent decode and the refusal, but not
that the rule meets the shapes a compiler emits, and until now there was
no ARM64 PE among the bundled samples at all: scanning them for a PE
machine field finds eight i386, four AMD64 and one ReadyToRun image, and
no 0xAA64.

The fixture is built from source rather than taken from a system, so it
can be redistributed and rebuilt:

    aarch64-w64-mingw32-clang++ -O2 -g -gdwarf-4 probe.cpp -o probe.exe

with llvm-mingw 20250430. The source exercises catch funclets, cleanup
funclets and a [[noreturn]] cold path, the shapes that get their own
unwind record.

That also makes it a control. clang emits one .pdata record per named
function and splits none of them, so the over-seeding an MSVC-built ARM64
PE shows does not occur here; anything refused is refused on the extent
evidence rather than on a compiler's chunking habit. Eight addresses move,
each interior to a RUNTIME_FUNCTION extent whose own function the analysis
recovered, and they are listed rather than counted so a change that
refuses a different set fails instead of staying green on a total.

XORed like every other bundled sample. 200K, against 1.1M for the largest
already carried.
@danielplohmann

Copy link
Copy Markdown
Owner

Reviewed here and good to go. Verified the fixture end to end end to end: MZ, PE\x00\x00, machine 0xAA64, PE32+ console, 13 sections, 202,752 bytes after the XOR, and the DWARF paths name /opt/llvm-mingw-20250430-ucrt-ubuntu-22.04-x86_64, which matches the toolchain the docstring claims. No local or personal paths in it beyond the toolchain's own.

Listing the eight addresses rather than counting them is the part that makes this a regression test instead of a tally, and asserting on - off empty as well is what stops it from passing on a rule that refuses something new.

One non-blocking suggestion. The claim the fixture rests on is "built from source, so it can be redistributed and rebuilt", and the command line is in the docstring but probe.cpp is not in the tree — so the rebuild is reproducible only for you. No fixture in tests/ ships its source today, so this is a convention worth starting rather than one you broke: the source is small enough to sit in the docstring, or in a tests/sources/ file next to it. Whenever it is convenient, and it does not need its own PR.

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.

2 participants