[GH-ISSUE #6578] [Bug] Managed CIL labels a field MemberRef loaded by ldtoken as a method #3967

Closed
opened 2026-10-31 00:45:40 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6578

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

The production inspectManagedMembersBytes boundary returns contradictory facts for a field MemberRef used by ldtoken: member_refs[0].signature.kind is field, but the instruction anchor for that same token has operand_kind: "method". Coverage is complete, without an issue, and the method body is present.

This was executed on current main with an in-memory managed PE fixture. The same hardcoded MemberRef classification is present in the published 6.7.0 source; release runtime behavior was not separately executed.

Reproduction

From the audited checkout root, with its pinned Node toolchain and locked dependencies installed, run this exact reduced probe:

./node_modules/.bin/tsx --eval '
import { buildManagedPeFixture, managedPeFixtureTarget } from "./src/dotnet/ManagedPe.fixture.ts";
import { inspectManagedMembersBytes } from "./src/dotnet/ManagedMemberInspector.ts";
const il = Buffer.from([0xd0, 1, 0, 0, 0x0a, 0x26, 0x2a]);
const bytes = buildManagedPeFixture({targetFramework: null, fieldName: "Counter", fieldSignature: Buffer.from([0x06, 0x08]), memberRefName: "Counter", memberRefParentRaw: 8, memberRefSignature: Buffer.from([0x06, 0x08]), methodSignature: Buffer.from([0, 0, 1]), methodFlags: 0x96, ilBody: Buffer.concat([Buffer.from([(il.length << 2) | 2]), il])});
const result = inspectManagedMembersBytes(bytes, managedPeFixtureTarget(bytes));
console.log(JSON.stringify({coverage: result.coverage, field: {token: result.fields[0].token, name: result.fields[0].name, kind: result.fields[0].signature.kind}, memberRefKind: result.member_refs[0].signature.kind, anchor: result.methods[0].body.anchors[0]}));
'

The CIL is ldtoken 0x0a000001; pop; ret. The MemberRef has FieldSig bytes 06 08 (an int32 field) and names Counter on the same TypeDef that declares the fixture's matching Counter field. Method flags/signature describe a static void method. The probe constructs bytes and inspects them; it does not load or run the assembly.

Expected behavior

The ldtoken operand should be classified as field when its MemberRef signature establishes a field. If the metadata cannot resolve the kind, retain an unknown/token classification rather than asserting method.

Microsoft's ldtoken documentation explicitly permits field references, method references and type references. A MemberRef table token by itself does not distinguish fields from methods. REA already resolves the fixture MemberRef to signature.kind: "field" in the same inspection.

Impact and scope

Consumers trusting typed instruction anchors receive the wrong kind for field handles represented with MemberRef tokens, including ordinary references to fields outside the current module. Only the reduced local fixture was executed; production frequency and cross-module files were not measured.

The raw token and correctly decoded member_refs entry remain available. A caller can join the anchor's operand to member_refs[].token and use signature.kind as a safe workaround. The problem is bounded to the classification of the anchor; this report does not claim a call-edge error, execution failure or cross-build digest guarantee.

REA version

Executed current main: 46111dd091f2ae01baab9eb119b53b9bd309a05b (package metadata 6.7.0). Source inspection also finds the same 0x0a → method branch at published rea-agents-6.7.0 revision 27dc42d10249f0410904a75c0997842eb03b931c. This is not a main-only code addition; only main was executed.

Runtime and platform

Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64. No .NET runtime, native deep-analysis provider or target process was used.

Target and artifact context

Synthetic managed PE with one int32 field, a matching field MemberRef and a tiny CIL body containing ldtoken; pop; ret.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Executed reduced probe output:

{"coverage":{"state":"complete","issues":[]},"field":{"token":"0x04000001","name":"Counter","kind":"field"},"memberRefKind":"field","anchor":{"il_offset":0,"opcode":"ldtoken","operand_kind":"method","operand":"0x0a000001"}}

The initial three-case probe also established that a method MemberRef control is classified as method, while loading a FieldDef token 0x04000001 is classified as field. The faulty case is a field encoded through MemberRef.

Source: tokenKind at the audited SHA classifies every token starting 0x0a as method. The token operand reader uses that classification for ldtoken.

The published-release source contains the same classification. That establishes release-source applicability, not an executed release reproduction.

Typed outcome or recovery expected

For this resolved field MemberRef: {"opcode":"ldtoken","operand_kind":"field","operand":"0x0a000001"}. Preserve the fact that obtaining a handle is not a field access or method call.

Analysis or suspected cause

Source-derived: tokenKind chooses a semantic member kind using only the metadata table prefix. Unlike FieldDef and MethodDef, MemberRef contains both kinds. Its parsed signature must resolve the distinction, or the anchor must remain nonspecific. The existing MemberRef parser already retains that distinction.

Additional context

Open-issue title/body searches covered ldtoken, field memberref, memberref token and operand_kind. The matches concern FieldRVA support (#3829), joining instruction anchors to edges (#5151), and omitted operand-free instructions (#5147), rather than field MemberRef classification. The submitting agent additionally searched open and closed issues for the same behavior and found no matching report.

NOT_ENOUGH_INFO: no public CLI/MCP call, CLR execution, compiler-produced assembly, or separate release-runtime run was performed. The production inspection boundary and the complete result quoted above were executed before drafting.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Complete managed inspection returns an incorrect field-versus-method fact for a valid field MemberRef handle. The raw reference survives and the same result exposes the correct signature kind, bounding the observed impact.
  • Workaround status: available
  • Workaround basis: The executed result retains both anchor operand 0x0a000001 and the same-token MemberRef with decoded field signature; consumers can resolve that join instead of trusting the anchor's incorrect kind.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6578 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior The production `inspectManagedMembersBytes` boundary returns contradictory facts for a field MemberRef used by `ldtoken`: `member_refs[0].signature.kind` is `field`, but the instruction anchor for that same token has `operand_kind: "method"`. Coverage is `complete`, without an issue, and the method body is `present`. This was executed on current main with an in-memory managed PE fixture. The same hardcoded MemberRef classification is present in the published 6.7.0 source; release runtime behavior was not separately executed. ### Reproduction From the audited checkout root, with its pinned Node toolchain and locked dependencies installed, run this exact reduced probe: ```sh ./node_modules/.bin/tsx --eval ' import { buildManagedPeFixture, managedPeFixtureTarget } from "./src/dotnet/ManagedPe.fixture.ts"; import { inspectManagedMembersBytes } from "./src/dotnet/ManagedMemberInspector.ts"; const il = Buffer.from([0xd0, 1, 0, 0, 0x0a, 0x26, 0x2a]); const bytes = buildManagedPeFixture({targetFramework: null, fieldName: "Counter", fieldSignature: Buffer.from([0x06, 0x08]), memberRefName: "Counter", memberRefParentRaw: 8, memberRefSignature: Buffer.from([0x06, 0x08]), methodSignature: Buffer.from([0, 0, 1]), methodFlags: 0x96, ilBody: Buffer.concat([Buffer.from([(il.length << 2) | 2]), il])}); const result = inspectManagedMembersBytes(bytes, managedPeFixtureTarget(bytes)); console.log(JSON.stringify({coverage: result.coverage, field: {token: result.fields[0].token, name: result.fields[0].name, kind: result.fields[0].signature.kind}, memberRefKind: result.member_refs[0].signature.kind, anchor: result.methods[0].body.anchors[0]})); ' ``` The CIL is `ldtoken 0x0a000001; pop; ret`. The MemberRef has FieldSig bytes `06 08` (an `int32` field) and names `Counter` on the same TypeDef that declares the fixture's matching `Counter` field. Method flags/signature describe a static void method. The probe constructs bytes and inspects them; it does not load or run the assembly. ### Expected behavior The `ldtoken` operand should be classified as `field` when its MemberRef signature establishes a field. If the metadata cannot resolve the kind, retain an unknown/token classification rather than asserting `method`. [Microsoft's ldtoken documentation](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.ldtoken?view=net-10.0#remarks) explicitly permits field references, method references and type references. A MemberRef table token by itself does not distinguish fields from methods. REA already resolves the fixture MemberRef to `signature.kind: "field"` in the same inspection. ### Impact and scope Consumers trusting typed instruction anchors receive the wrong kind for field handles represented with MemberRef tokens, including ordinary references to fields outside the current module. Only the reduced local fixture was executed; production frequency and cross-module files were not measured. The raw token and correctly decoded `member_refs` entry remain available. A caller can join the anchor's `operand` to `member_refs[].token` and use `signature.kind` as a safe workaround. The problem is bounded to the classification of the anchor; this report does not claim a call-edge error, execution failure or cross-build digest guarantee. ### REA version Executed current main: `46111dd091f2ae01baab9eb119b53b9bd309a05b` (package metadata 6.7.0). Source inspection also finds the same `0x0a` → `method` branch at published `rea-agents-6.7.0` revision `27dc42d10249f0410904a75c0997842eb03b931c`. This is not a main-only code addition; only main was executed. ### Runtime and platform Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64. No .NET runtime, native deep-analysis provider or target process was used. ### Target and artifact context Synthetic managed PE with one `int32` field, a matching field MemberRef and a tiny CIL body containing `ldtoken; pop; ret`. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Executed reduced probe output: ```json {"coverage":{"state":"complete","issues":[]},"field":{"token":"0x04000001","name":"Counter","kind":"field"},"memberRefKind":"field","anchor":{"il_offset":0,"opcode":"ldtoken","operand_kind":"method","operand":"0x0a000001"}} ``` The initial three-case probe also established that a method MemberRef control is classified as `method`, while loading a FieldDef token `0x04000001` is classified as `field`. The faulty case is a field encoded through MemberRef. Source: [tokenKind at the audited SHA](https://github.com/morluto/rea/blob/46111dd091f2ae01baab9eb119b53b9bd309a05b/src/dotnet/ManagedMemberInstructionDecoder.ts#L243-L250) classifies every token starting `0x0a` as `method`. [The token operand reader](https://github.com/morluto/rea/blob/46111dd091f2ae01baab9eb119b53b9bd309a05b/src/dotnet/ManagedMemberInstructionDecoder.ts#L426-L435) uses that classification for `ldtoken`. [The published-release source](https://github.com/morluto/rea/blob/27dc42d10249f0410904a75c0997842eb03b931c/src/dotnet/ManagedMemberInstructionDecoder.ts) contains the same classification. That establishes release-source applicability, not an executed release reproduction. ### Typed outcome or recovery expected For this resolved field MemberRef: `{"opcode":"ldtoken","operand_kind":"field","operand":"0x0a000001"}`. Preserve the fact that obtaining a handle is not a field access or method call. ### Analysis or suspected cause Source-derived: `tokenKind` chooses a semantic member kind using only the metadata table prefix. Unlike FieldDef and MethodDef, MemberRef contains both kinds. Its parsed signature must resolve the distinction, or the anchor must remain nonspecific. The existing MemberRef parser already retains that distinction. ### Additional context Open-issue title/body searches covered `ldtoken`, `field memberref`, `memberref token` and `operand_kind`. The matches concern FieldRVA support (#3829), joining instruction anchors to edges (#5151), and omitted operand-free instructions (#5147), rather than field MemberRef classification. The submitting agent additionally searched open and closed issues for the same behavior and found no matching report. NOT_ENOUGH_INFO: no public CLI/MCP call, CLR execution, compiler-produced assembly, or separate release-runtime run was performed. The production inspection boundary and the complete result quoted above were executed before drafting. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Complete managed inspection returns an incorrect field-versus-method fact for a valid field MemberRef handle. The raw reference survives and the same result exposes the correct signature kind, bounding the observed impact. - Workaround status: available - Workaround basis: The executed result retains both anchor operand `0x0a000001` and the same-token MemberRef with decoded field signature; consumers can resolve that join instead of trusting the anchor's incorrect kind.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3967
No description provided.