Skip to content

Review fixes; reject bad arm64 operands - #13

Merged
scizzydo merged 5 commits into
masterfrom
arm-strict-operands
Oct 13, 2026
Merged

scizzydo merged 5 commits into
masterfrom
arm-strict-operands

Conversation

@scizzydo

Copy link
Copy Markdown
Owner

Two commits:

9456e1c: review fixes

  • X docs
  • bounded parsing
  • no-exceptions builds
  • arm64 wildcard fixes

a36d7f6: strict arm64 operands

  • A bad operand is now a compile error for _armpattern/ARM_PATTERN. RuntimeArmPattern throws instead, and the message names the line. Bad operands include:

    • a garbage or out-of-range register (X31, X32)
    • the wrong register kind or width
    • an out-of-range or misaligned immediate
    • a missing or extra operand
    • an unknown shift, extend or condition
    • text after the last operand

    Before, the line silently became a wildcard, or an out-of-range value was truncated into the wrong encoding. Only an unknown mnemonic still becomes a wildcard.

  • ; comments are stripped, so lines copied from IDA or lldb with trailing comments still parse.

  • BIC/BICS/ORN/EON with an immediate encode as AND/ANDS/ORR/EOR with the inverted value. Checked against clang.

  • Negative W logical immediates encode (AND W0, W1, #-0x1000).

  • Fix for an existing leak: GCC -O0 (-fkeep-static-consts) emitted the unreferenced parse tables, which pulled every parser and mnemonic into binaries that only use compile-time patterns. The tables are now inline constexpr.

Testing

  • ctest 24/24 on gcc and clang. This includes the new test_arm_compile_time_cpp{17,20}: the bad build must fail to compile, and the encoder must not end up in the -O0 binary.
  • test_arm passes natively on an Apple arm64 machine.
  • The IDA plugin test passes, including a check that the error names the bad line.
  • ASan/UBSan are clean on about 1M fuzzed lines.
  • Fuzzing with GNU as as the reference:
    • 43.5k valid lines encode exactly as before.
    • In the mutation fuzz, no line as rejects is accepted.
    • The parser is stricter than as on purpose in three places, matching the Arm spec and LLVM:
      • as truncates large values to 32 bits.
      • as allows mismatched register widths with an extend.
      • as accepts a ! after a non-memory operand.

Not tested on MSVC.

🤖 Generated with Claude Code

scizzydo and others added 5 commits October 12, 2026 14:32
…, arm64 wildcards

- README: X<n> is the bytes left in the instruction (for /d); the operand size is /d1 /r2 /r8...
  The jb short example read 4 bytes instead of 1.
- RuntimePattern(p, len) read flags and X<n> past len on unterminated text.
- Builds with -fno-exceptions: errors are compile errors at compile time, a trap at runtime.
- arm64 /d on an instruction it can't decode returns nullptr instead of throwing from find();
  the IDA plugin reports such hits as unresolved.
- C++17: negative ADR/ADRP offsets failed to compile; the /d decoders shifted negative values.
- ADD/SUB/CMP #imm, LSL #? matched only LSL #0.
- Bitmask immediates and MOV with a bare ? Rd encoded only the 32-bit form; MOV truncated
  64-bit values (MOV ?, #0xFFFFFFFF00000000 matched MOV X0, #0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bad operand (garbage or out-of-range register, wrong register kind or
width, out-of-range or misaligned immediate, missing/extra operands,
unknown shift/extend/condition, trailing text) is now a compile error for
_armpattern/ARM_PATTERN and a throw naming the line for RuntimeArmPattern.
Only an unknown mnemonic stays a wildcard.

- ';' comments are stripped from lines
- BIC/BICS/ORN/EON immediates encode as AND/ANDS/ORR/EOR of the inverse
- negative W logical immediates encode
- parse tables are inline constexpr: GCC -O0 (-fkeep-static-consts)
  emitted them unreferenced, pulling every parser and mnemonic into
  binaries using only compile-time patterns
- test_arm_compile_time: bad operand must fail to compile; -O0 binary
  must not contain encoder strings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ond offsets at runtime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… comment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s in comments

- A hex immediate past INT64_MAX wrapped negative (ADD X0, X1, #0xFFFFFFFFFFFFFFFF
  encoded SUB #1; LDR [X1, #0xFFFFFFFFFFFFFFF8] became LDUR #-8), and a negative one
  past -2^63 wrapped positive (MOV X0, #-0xFFFFFFFFFFFFFFFF was MOV #1). Both fail now.
- ADRP takes a page distance: an offset that isn't a multiple of 0x1000 fails instead
  of being truncated (ADRP X0, #0x1234 matched page 1).
- BIC/BICS/ORN/EON #? and immediates with ? registers pinned immr's top bit to 0,
  missing 64-bit immediates like AND X0, X1, #0xFFFFFFFF00000000.
- The flags line ends at a ';' comment or newline: "/a ; done" turned on /d.
- The two _bad compile tests build in the same build directory: RESOURCE_LOCK, so
  ctest -j doesn't run two ninjas there at once (a failed build would pass).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@scizzydo
scizzydo merged commit 9532db0 into master Oct 13, 2026
3 checks passed
@scizzydo
scizzydo deleted the arm-strict-operands branch October 13, 2026 02:43
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.

1 participant