[GH-ISSUE #6715] [Bug] IL2CPP counts one native registration record twice through consistent ELF load aliases #4082

Closed
opened 2026-10-31 00:46:41 +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/6715

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 IL2CPP application service rejects synthetic ELF registration data with ambiguous-code-gen-module after an additional PT_LOAD exposes the same virtual module record with the same file backing. The original fixture links four code addresses. An identical load alias and a partial alias both fail, although the shared container mapper resolves the record unambiguously to file offset 1024.

Reproduction

From the source checkout below with locked dependencies installed, feed this complete script to node --import tsx --input-type=module. This is the script executed on stdin; it creates and removes temporary synthetic files and never executes them.

node --import tsx --input-type=module <<'JS'
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { createElfFixture, createIl2cppBinaryFixture } from './tests/fixtures/il2cpp/binary.ts';
import { createIl2cppMetadataFixture } from './tests/fixtures/il2cpp/metadata.ts';
import { createIl2cppMetadataService } from './src/composition/il2cpp.ts';
import { readBinaryContainer } from './src/native/metadata/readBinaryContainer.ts';
import { mappedFileOffset } from './src/native/metadata/BinaryContainer.ts';
const dir = await mkdtemp(join(tmpdir(), 'rea-il2cpp-alias-'));
const path = join(dir, 'global-metadata.dat');
await writeFile(path, createIl2cppMetadataFixture({ version: 31 }).bytes);
console.log(JSON.stringify({node:process.version,platform:process.platform,architecture:process.arch}));
try {
 for (const name of ['control', 'identical-alias', 'partial-alias', 'distinct-module']) {
  const bytes = createElfFixture(createIl2cppBinaryFixture({duplicateModule:name==='distinct-module'}));
  if (name.includes('alias')) {
   bytes.copy(bytes,176,64,120);
   bytes.writeUInt16LE(3,56);
   if (name==='partial-alias') {
    bytes.writeBigUInt64LE(0x400n,184);
    bytes.writeBigUInt64LE(0x100000400n,192);
    bytes.writeBigUInt64LE(0x100000400n,200);
    bytes.writeBigUInt64LE(0x80n,208);
    bytes.writeBigUInt64LE(0x80n,216);
   }
  }
  const container=readBinaryContainer(bytes);
  const binary_path=join(dir,name+'.so');
  await writeFile(binary_path,bytes);
  const service=createIl2cppMetadataService();
  try {
   const result=await service.inspect({path,binary_path,detail:'complete'});
   console.log(JSON.stringify({name,mappings:container.mappings.length,moduleOffset:mappedFileOffset(container.mappings,0x100000400n,24),ok:result.ok,...(result.ok?{addresses:result.value.normalized_result.method_addresses.map(x=>x.address)}:{tag:result.error._tag,issues:result.error.issues?.map(x=>x.message.replaceAll(dir,'<fixture>'))})}));
  }finally{await service.close();}
 }
}finally{await rm(dir,{recursive:true,force:true});}
JS

The added program header occupies [176,232), before the fixture payload. The identical alias repeats the existing load range. The partial alias describes virtual [0x100000400,0x100000480) backed by file [0x400,0x480), consistently with the original load mapping. Each case in this complete script was observed once; earlier lower-level and application probes also reproduced the identical-alias refusal.

Expected behavior

Count a module record at one virtual address with consistent file backing once, preserving the control's native addresses. Continue refusing genuinely distinct matching records: the distinct-module control adds a second record at a different virtual address and should remain ambiguous.

The native-linking contract requires a single matching module record. REA's shared mapping owner accepts repeated mappings when they resolve to the same file offset; it refuses conflicting backing. The ELF program-header specification requires ascending load addresses but gives no uniqueness rule. Loader acceptance of these synthesized aliases was not tested.

Impact and scope

A selected binary with consistent overlapping mappings cannot produce linked method/type Evidence through this service. Both alias cases reproduced; the unmodified control succeeds. NOT_ENOUGH_INFO: occurrence in Unity-produced binaries, complete loader conformance, other native formats, and CLI/MCP delivery were not tested. This is a bounded synthetic parser inconsistency, not a claim that mainstream Unity linkers routinely emit this layout. No practical workaround for an existing selected binary was verified.

REA version

Main source 8c4f96ef4759fd78da66c4b9726825ca71d7ee0c; package manifest version 6.7.0. Main-only baseline: the latest published rea-agents-6.7.0 tag has no src/il2cpp implementation, so release impact is not claimed.

Runtime and platform

Node.js v26.12.1; Linux x64; locked source-checkout dependencies. No Unity installation or native analysis engine.

Target and artifact context

Self-made version 31 metadata and 64-bit little-endian AArch64 ELF bytes built from the repository's fixture builders. The fixture is parser input, not a runnable application. No target or native loader was executed.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The executed script printed:

{"node":"v26.12.1","platform":"linux","architecture":"x64"}
{"name":"control","mappings":1,"moduleOffset":1024,"ok":true,"addresses":["0x100000200","0x100000210","0x100000220","0x100000230",null]}
{"name":"identical-alias","mappings":2,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/identical-alias.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]}
{"name":"partial-alias","mappings":2,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/partial-alias.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]}
{"name":"distinct-module","mappings":1,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/distinct-module.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]}

<fixture> replaces only the generated temporary directory. The script itself performs that replacement. The service returned normally for all cases; the failure is its typed result, not a process crash.

Typed outcome or recovery expected

The two consistent-alias cases should return successful Evidence with the control's five method-address rows. The genuinely distinct record must continue to return the ambiguity error. These are restoration criteria; no fix was implemented.

Analysis or suspected cause

Source-derived: findModules enumerates every mapping independently, then treats any second candidate for an image as another module without comparing virtual record identity. Thus the same record is visited twice. Other registration scans use a similar enumeration pattern; they were not separately bypassed or tested and are not separate findings here.

Additional context

Read-only open/closed searches included IL2CPP in:title, IL2CPP alias, IL2CPP mapping, IL2CPP ambiguous, IL2CPP PT_LOAD, and code-gen-module. No matching report was identified before drafting. Open #6701 concerns adjacent ELF relocation widths, a different failure. An independent source/script review accepted this narrower parser inconsistency while explicitly leaving real-loader validity and prevalence unknown. NOT_ENOUGH_INFO: no last-known-good implementation was established.

Triage assessment

  • Impact level: P2
  • Assessment status: provisional
  • Impact basis: The production service rejects two consistent-mapping synthetic cases and loses the linked analysis result. The container owner treats their record identity as unambiguous; loader acceptance and prevalence in real Unity artifacts remain unknown.
  • Workaround status: unverified
  • Workaround basis: The control isolates duplicate mapping enumeration but editing a selected binary is not an established workflow. No alternative tool or input-preserving workaround was executed.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6715 ### 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 IL2CPP application service rejects synthetic ELF registration data with `ambiguous-code-gen-module` after an additional `PT_LOAD` exposes the same virtual module record with the same file backing. The original fixture links four code addresses. An identical load alias and a partial alias both fail, although the shared container mapper resolves the record unambiguously to file offset 1024. ### Reproduction From the source checkout below with locked dependencies installed, feed this complete script to `node --import tsx --input-type=module`. This is the script executed on stdin; it creates and removes temporary synthetic files and never executes them. ```sh node --import tsx --input-type=module <<'JS' import { mkdtemp, writeFile, rm } from 'node:fs/promises'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { createElfFixture, createIl2cppBinaryFixture } from './tests/fixtures/il2cpp/binary.ts'; import { createIl2cppMetadataFixture } from './tests/fixtures/il2cpp/metadata.ts'; import { createIl2cppMetadataService } from './src/composition/il2cpp.ts'; import { readBinaryContainer } from './src/native/metadata/readBinaryContainer.ts'; import { mappedFileOffset } from './src/native/metadata/BinaryContainer.ts'; const dir = await mkdtemp(join(tmpdir(), 'rea-il2cpp-alias-')); const path = join(dir, 'global-metadata.dat'); await writeFile(path, createIl2cppMetadataFixture({ version: 31 }).bytes); console.log(JSON.stringify({node:process.version,platform:process.platform,architecture:process.arch})); try { for (const name of ['control', 'identical-alias', 'partial-alias', 'distinct-module']) { const bytes = createElfFixture(createIl2cppBinaryFixture({duplicateModule:name==='distinct-module'})); if (name.includes('alias')) { bytes.copy(bytes,176,64,120); bytes.writeUInt16LE(3,56); if (name==='partial-alias') { bytes.writeBigUInt64LE(0x400n,184); bytes.writeBigUInt64LE(0x100000400n,192); bytes.writeBigUInt64LE(0x100000400n,200); bytes.writeBigUInt64LE(0x80n,208); bytes.writeBigUInt64LE(0x80n,216); } } const container=readBinaryContainer(bytes); const binary_path=join(dir,name+'.so'); await writeFile(binary_path,bytes); const service=createIl2cppMetadataService(); try { const result=await service.inspect({path,binary_path,detail:'complete'}); console.log(JSON.stringify({name,mappings:container.mappings.length,moduleOffset:mappedFileOffset(container.mappings,0x100000400n,24),ok:result.ok,...(result.ok?{addresses:result.value.normalized_result.method_addresses.map(x=>x.address)}:{tag:result.error._tag,issues:result.error.issues?.map(x=>x.message.replaceAll(dir,'<fixture>'))})})); }finally{await service.close();} } }finally{await rm(dir,{recursive:true,force:true});} JS ``` The added program header occupies `[176,232)`, before the fixture payload. The identical alias repeats the existing load range. The partial alias describes virtual `[0x100000400,0x100000480)` backed by file `[0x400,0x480)`, consistently with the original load mapping. Each case in this complete script was observed once; earlier lower-level and application probes also reproduced the identical-alias refusal. ### Expected behavior Count a module record at one virtual address with consistent file backing once, preserving the control's native addresses. Continue refusing genuinely distinct matching records: the `distinct-module` control adds a second record at a different virtual address and should remain ambiguous. The [native-linking contract](https://github.com/morluto/rea/blob/8c4f96ef/docs/il2cpp-metadata.md#L202-L210) requires a single matching module record. REA's [shared mapping owner](https://github.com/morluto/rea/blob/8c4f96ef/src/native/metadata/BinaryContainer.ts#L209-L260) accepts repeated mappings when they resolve to the same file offset; it refuses conflicting backing. The [ELF program-header specification](https://gabi.xinuos.com/elf/07-pheader.html) requires ascending load addresses but gives no uniqueness rule. Loader acceptance of these synthesized aliases was not tested. ### Impact and scope A selected binary with consistent overlapping mappings cannot produce linked method/type Evidence through this service. Both alias cases reproduced; the unmodified control succeeds. NOT_ENOUGH_INFO: occurrence in Unity-produced binaries, complete loader conformance, other native formats, and CLI/MCP delivery were not tested. This is a bounded synthetic parser inconsistency, not a claim that mainstream Unity linkers routinely emit this layout. No practical workaround for an existing selected binary was verified. ### REA version Main source `8c4f96ef4759fd78da66c4b9726825ca71d7ee0c`; package manifest version 6.7.0. Main-only baseline: the latest published `rea-agents-6.7.0` tag has no `src/il2cpp` implementation, so release impact is not claimed. ### Runtime and platform Node.js v26.12.1; Linux x64; locked source-checkout dependencies. No Unity installation or native analysis engine. ### Target and artifact context Self-made version 31 metadata and 64-bit little-endian AArch64 ELF bytes built from the repository's fixture builders. The fixture is parser input, not a runnable application. No target or native loader was executed. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The executed script printed: ```jsonl {"node":"v26.12.1","platform":"linux","architecture":"x64"} {"name":"control","mappings":1,"moduleOffset":1024,"ok":true,"addresses":["0x100000200","0x100000210","0x100000220","0x100000230",null]} {"name":"identical-alias","mappings":2,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/identical-alias.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]} {"name":"partial-alias","mappings":2,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/partial-alias.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]} {"name":"distinct-module","mappings":1,"moduleOffset":1024,"ok":false,"tag":"AnalysisInputError","issues":["Selected binary <fixture>/distinct-module.so does not link to the metadata: ambiguous-code-gen-module: multiple records match code generation module Assembly-CSharp.dll"]} ``` `<fixture>` replaces only the generated temporary directory. The script itself performs that replacement. The service returned normally for all cases; the failure is its typed result, not a process crash. ### Typed outcome or recovery expected The two consistent-alias cases should return successful Evidence with the control's five method-address rows. The genuinely distinct record must continue to return the ambiguity error. These are restoration criteria; no fix was implemented. ### Analysis or suspected cause Source-derived: [findModules](https://github.com/morluto/rea/blob/8c4f96ef/src/il2cpp/Il2cppBinaryRegistration.ts#L126-L153) enumerates every mapping independently, then treats any second candidate for an image as another module without comparing virtual record identity. Thus the same record is visited twice. Other registration scans use a similar enumeration pattern; they were not separately bypassed or tested and are not separate findings here. ### Additional context Read-only open/closed searches included `IL2CPP in:title`, `IL2CPP alias`, `IL2CPP mapping`, `IL2CPP ambiguous`, `IL2CPP PT_LOAD`, and `code-gen-module`. No matching report was identified before drafting. Open #6701 concerns adjacent ELF relocation widths, a different failure. An independent source/script review accepted this narrower parser inconsistency while explicitly leaving real-loader validity and prevalence unknown. NOT_ENOUGH_INFO: no last-known-good implementation was established. ## Triage assessment - Impact level: P2 - Assessment status: provisional - Impact basis: The production service rejects two consistent-mapping synthetic cases and loses the linked analysis result. The container owner treats their record identity as unambiguous; loader acceptance and prevalence in real Unity artifacts remain unknown. - Workaround status: unverified - Workaround basis: The control isolates duplicate mapping enumeration but editing a selected binary is not an established workflow. No alternative tool or input-preserving workaround was executed.
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#4082
No description provided.