fix: HLIL member_index masking + operand-count table (root cause)#24
Merged
Conversation
Python's _get_member_index masks the sentinel (1<<63) and returns None when there is no named member; the binding returned the raw ulong and never returned null. Investigation showed the deeper root cause: the OperationOperands table undercounted the three field instructions by omitting the member_index slot, so RawOperands was one short and ANY operand access on them (including the existing Offset) threw IndexOutOfRangeException. member_index was therefore never readable at all. - OperationOperands: HLIL_STRUCT_FIELD 2->3, HLIL_DEREF_FIELD 2->3, HLIL_DEREF_FIELD_SSA 3->4 (source, offset/srcMem, member_index). Growing the count only adds the member_index slot; existing operand indices are unchanged. - HighLevelILInstruction.MemberIndexFromRawOperand: masks the (1<<63) sentinel to null, mirroring Python _get_member_index. - HLILStructField.MemberIndex, HLILDerefField.MemberIndex, HLILDerefFieldSSA.MemberIndex: long? via the helper. The two Deref members are renamed from MemoryIndex for parity with StructField (no internal callers).
This was referenced Jul 8, 2026
tinysec
added a commit
that referenced
this pull request
Jul 8, 2026
) Closes HIGH missing-capi gap #24. The binding declared the BNRustSimplifyStrToFQN P/Invoke (which returns a BNQualifiedName by value) but declined to expose it: two TODO notes warned the struct-return (sret) ABI made the binding "non-trivial" and redirected callers to the string-only RustSimplifyStrToStr, so Python's demangle.simplify_name_to_qualified_name (demangle.py:253) had no managed equivalent. The sret concern was unfounded: BNQualifiedName is blittable (two IntPtrs plus a ulong), so the P/Invoke marshaller handles the struct-return ABI directly -- the same pattern as BNTypeBuilderGetStructureName. Verified end-to-end: a templated C++ name collapses identically to Python. Add Core.SimplifyNameToQualifiedName with a string overload (simplify flag, defaults true) and a QualifiedName overload (Python forces simplify=true for qualified-name input). Both wrap via QualifiedName.TakeNative (reads components eagerly, then frees the native array) and return null when the simplifier yields no components. Removes the two stale TODO notes.
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.
Summary
Python's
_get_member_indexmasks the sentinel (1<<63) and returnsNonewhen there is no named member; the binding returned the rawulongand never returned null.Investigation surfaced the deeper root cause: the
OperationOperandstable undercounted the three field instructions by omitting themember_indexslot, soRawOperandswas one short and any operand access on them (including the existingOffset) threwIndexOutOfRangeException.member_indexwas therefore never readable at all.Changes
OperationOperands— corrected counts:HLIL_STRUCT_FIELD: 2 → 3 (source, offset, member_index)HLIL_DEREF_FIELD: 2 → 3HLIL_DEREF_FIELD_SSA: 3 → 4 (source, srcMem, offset, member_index)Growing the count only adds the
member_indexslot; existing operand indices are unchanged, soOffset/Source/SourceMemoryare unaffected.HighLevelILInstruction.MemberIndexFromRawOperand— masks the (1<<63) sentinel tonull, mirroring Python_get_member_index.HLILStructField.MemberIndex,HLILDerefField.MemberIndex,HLILDerefFieldSSA.MemberIndex— nowlong?via the helper. The two Deref members are renamed fromMemoryIndexfor parity withStructField(no internal callers reference the old name).Verification
MemberIndexisnullor a non-negative index (never the leaked sentinel) and that cat's no-named-member struct field surfaces asnull, matching Pythonmember_index == None.