Skip to content

fix: MLIL OperationOperands undercounts (27 ops) + bogus VAR_PHI.SourceMemory#28

Merged
tinysec merged 1 commit into
masterfrom
fix/mlil-operand-count-table
Jul 8, 2026
Merged

fix: MLIL OperationOperands undercounts (27 ops) + bogus VAR_PHI.SourceMemory#28
tinysec merged 1 commit into
masterfrom
fix/mlil-operand-count-table

Conversation

@tinysec

@tinysec tinysec commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Problem

The OperationOperands count table in Struct/BNMediumLevelILInstruction.cs sizes each instruction's RawOperands[] array. 27 entries undercounted their operands, so accessing the affected typed-accessor property threw IndexOutOfRangeException.

Also: MLIL_VAR_PHI.SourceMemory was a bogus property that read RawOperands[1] (the dest SSA version slot) and mislabeled it as memory — C++ MLIL_VAR_PHI has no source-memory operand (only MEM_PHI does).

Root cause

Counts verified 1:1 against mediumlevelilinstruction.h raw layouts (C# GetOperandAs index == C++ GetRawOperandAs index; SSAVariable = 2 slots). Misses by group:

  • TwoOperandWithCarry (ADC/SBB/RLC/RRC): omitted carry → 2→3
  • SSA stores missing memory-version slots: CALL_SSA, SYSCALL_SSA, TAILCALL_SSA, STORE_SSA, SET_VAR_*_SSA, MEMORY_INTRINSIC_SSA
  • SSAVariable second-slot: FORCE_VER_SSA, SET_VAR_SSA, FREE_VAR_SLOT_SSA, VAR_SPLIT_SSA
  • Phi/partial: MEM_PHI, VAR_PHI, SET_VAR_ALIASED*, VAR_ALIASED*
  • ASSERT 1→2, FORCE_VER 1→2

Completes the OperationOperands cluster (HLIL was PR #26, LLIL was PR #27).

Deferred (separate follow-up PRs)

  1. 3 non-SSA untyped variants (CALL_UNTYPED/SYSCALL_UNTYPED/TAILCALL_UNTYPED): their non-SSA output is a 2-slot expr_list (no non-SSA MLIL_CALL_OUTPUT op exists), so their accessors are off-by-one, not merely undercounted. Needs accessor surgery (wrapper→direct-list); left crashing rather than silently returning shifted data.
  2. MLIL_JUMP_TO.Targets index-map reader: GetOperandAsIntegerMap wrongly calls BNMediumLevelILGetOperandList (the expr-list reader); C++ uses GetRawOperandAsIndexMap (operands[count@1, offset@2]). Returns a malformed array → OOB. Separate gap (missing index-map native accessor).

Changes

  • Struct/BNMediumLevelILInstruction.cs: 27 count bumps, each with an operand-list comment.
  • MediumLevelIL/MLILVariablePhi.cs: removed bogus SourceMemory.

Verification

  • dotnet build -c Release clean.
  • Static audit (crash_audit_mlil.py): the 27 fixed ops now clear; only the 3 deferred untyped variants remain (tracked).
  • New E2E regression in the harness repo: flat MustGetExpression sweep over cat.bndb MLIL, invokes each declared operand-accessor getter (index-map getters excluded as a separate gap), asserts none throws IndexOutOfRangeException.
  • Full harness regression: 42/42 pass.

…ceMemory (#28)

The OperationOperands count table in Struct/BNMediumLevelILInstruction.cs
sized the per-instruction RawOperands[] array too small for 27 operations,
so accessing the affected typed-accessor property threw IndexOutOfRange.
Groups: TwoOperandWithCarry (ADC/SBB/RLC/RRC 2->3); SSA stores missing
memory-version slots (CALL_SSA, SYSCALL_SSA, TAILCALL_SSA, STORE_SSA,
SET_VAR_*_SSA, MEMORY_INTRINSIC_SSA); SSAVariable second-slot (FORCE_VER_SSA,
SET_VAR_SSA, FREE_VAR_SLOT_SSA, VAR_SPLIT_SSA); phi/partial (MEM_PHI,
VAR_PHI, SET_VAR_ALIASED*, VAR_ALIASED*); carry (ASSERT 1->2); FORCE_VER 1->2.

Also removed the bogus MLIL_VAR_PHI.SourceMemory property: it read
RawOperands[1] (the dest SSA version slot) and mislabeled it as memory --
C++ MLIL_VAR_PHI has no source-memory operand (only MEM_PHI does).

Counts verified 1:1 against mediumlevelilinstruction.h raw layouts (C#
GetOperandAs index == C++ GetRawOperandAs index; SSAVariable = 2 slots).
The 3 non-SSA untyped variants (CALL_UNTYPED/SYSCALL_UNTYPED/TAILCALL_UNTYPED)
are deferred: their non-SSA output is a 2-slot expr_list (no non-SSA
MLIL_CALL_OUTPUT op exists), so their accessors are off-by-one, not merely
undercounted -- a follow-up accessor PR will address them.
@tinysec tinysec merged commit 4e412da into master Jul 8, 2026
1 check passed
@tinysec tinysec deleted the fix/mlil-operand-count-table branch July 8, 2026 08:23
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