[GH-ISSUE #5700] [Bug] Recorded-crash inspection applies CORE note names to GNU-owned notes #3450

Closed
opened 2026-10-31 00:40:36 +00:00 by bsakaguchi · 1 comment
Owner

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

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

inspect-recorded-crash assigns CORE note names to GNU-owned notes in an admitted Linux x86-64 core. GNU/type 3 becomes NT_PRPSINFO instead of GNU build-id metadata. A GNU-owned note with numeric type 0x46494c45 becomes NT_FILE, causing --debugger-context to reject it for a missing mapping-table header before debugger configuration is checked.

An independent verifier reproduced both effects with the unmodified public CLI on Linux x64. Original owner/descriptor bytes remain available, including in the failure's partial observation. This is incorrect structured metadata and a false enrichment refusal; no security-boundary escape was demonstrated.

Reproduction

Run from the prepared source checkout on Linux x64 with Node.js, Python, procps-compatible ps, GNU readelf, and jq available. The caller-supplied Python profile below matches the documented dependencies. The three debugger-context commands intentionally exit nonzero; use a shell that continues after failure.

work_dir="$(mktemp -d)"
mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" "$work_dir/state" "$work_dir/tmp"
python3 -m venv "$work_dir/venv"
"$work_dir/venv/bin/python" -m pip install pwntools==4.15.0 pyelftools==0.33 unicorn==2.1.2
python3 - "$work_dir" <<'PY'
import pathlib, struct, sys
out = pathlib.Path(sys.argv[1])
def note(owner, kind, descriptor):
    def padded(data):
        return data + bytes((-len(data)) % 4)
    return struct.pack('<III',len(owner),len(descriptor),kind) + padded(owner) + padded(descriptor)
status = bytearray(336)
struct.pack_into('<h',status,12,11)
struct.pack_into('<i',status,32,123)
struct.pack_into('<Q',status,240,0x401234)
common = note(b'CORE\0',1,status)
for name, owner, kind, desc in [
    ('buildid',b'GNU\0',3,bytes(range(20))),
    ('mapping',b'GNU\0',0x46494c45,b''),
    ('unknown-control',b'GNU\0',0x12345678,b''),
    ('core-mapping-control',b'CORE\0',0x46494c45,b''),
    ('core-type3-control',b'CORE\0',3,bytes(136)),
]:
    notes = common + note(owner,kind,desc)
    ident = b'\x7fELF' + bytes([2,1,1,3,0]) + bytes(7)
    header = struct.pack('<16sHHIQQQIHHHHHH',ident,4,62,1,0,64,0,0,64,56,1,64,0,0)
    segment = struct.pack('<IIQQQQQQ',4,4,120,0,0,len(notes),len(notes),4)
    (out / (name+'.core')).write_bytes(header+segment+notes)
PY
inspect_core() {
  env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \
    XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \
    XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \
    REA_PWNTOOLS_PYTHON="$work_dir/venv/bin/python" \
    node scripts/rea.mjs inspect-recorded-crash "$@" --json
}
inspect_core "$work_dir/buildid.core" > "$work_dir/buildid.json"
jq -c '.normalized_result.notes[] | {owner_display, type}' "$work_dir/buildid.json"
inspect_core "$work_dir/core-type3-control.core" > "$work_dir/core-type3.json"
jq -c '.normalized_result.notes[] | {owner_display, type}' "$work_dir/core-type3.json"
inspect_core "$work_dir/mapping.core" --debugger-context
inspect_core "$work_dir/unknown-control.core" --debugger-context
inspect_core "$work_dir/core-mapping-control.core" --debugger-context
readelf --notes "$work_dir/buildid.core" "$work_dir/mapping.core"

The verifier executed the equivalent isolated runner once per fixture at the baseline below. GDB and pwndbg were intentionally absent: the unknown-type control reaches their genuine configuration failure, while the colliding GNU note fails earlier at the mapping guard. No platform override or production-code change was used.

Expected behavior

Preserve owner-qualified note identity. GNU/type 3 should retain a GNU build-id name or numeric type 3. An unfamiliar GNU-owned type should remain opaque. Only a genuine CORE mapping note should receive Linux mapping-table validation. Preserve existing CORE/type-3 naming and genuine CORE mapping confinement checks.

The recorded-crash contract promises original identity for unfamiliar note owners and kinds; the same guide documents this supported ELF profile.

Impact and scope

P2: supported inputs return misleading note identities, and one owner/type collision blocks the optional debugger-context facet. Basic decoded evidence, raw bytes, and the original recording remain available. Omitting debugger context or using readelf provides partial mitigation but does not restore the requested enrichment; an enrichment-preserving workaround is unverified. Real-producer prevalence, other owners/architectures, MCP execution, and successful full debugger enrichment were not tested.

REA version

rea-agents 6.7.0, source 9d8e7803326a382cbbb3ffdb900f5e93e5442d68, prepared compiled CLI. Submission recheck fetched origin/main at 52c84f6f58fa3cc7b39326654c6d3f261f957957: none of the cited expected/failure source files changed since the baseline. This was a source freshness check, not another runtime replay on main.

Runtime and platform

Verified runtime: Node.js v24.18.0, Linux x64 in an explicitly selected linux/amd64 Docker container, Python 3.12, pwntools 4.15.0, pyelftools 0.33, Unicorn 2.1.2, GNU readelf 2.44, procps installed. Docker client/Engine 29.9.1; arm64 Engine host. Submission host checks: Node.js v26.12.0; macOS 27.0.1; arm64. The CLI reproduction ran inside Linux, not on that macOS host.

Target and artifact context

Synthetic bytes only: ELF64 little-endian x86-64 ET_CORE, GNU/Linux OSABI, ABI version zero, one PT_NOTE segment, a 336-byte CORE/NT_PRSTATUS descriptor and one varied owner/type note. No live target, PT_LOAD segment, real crash data, or target execution is needed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The verified build-id invocation exits 0 and reports:

{"owner_display":"CORE","type":"NT_PRSTATUS"}
{"owner_display":"GNU","type":"NT_PRPSINFO"}

GNU readelf identifies that same second note as NT_GNU_BUILD_ID, build ID 000102030405060708090a0b0c0d0e0f10111213. The CORE/type-3 control exits 0 and correctly reports NT_PRPSINFO. The JSON note record has no separate numeric-type field, although the retained artifact/source range still permits reading the original note header.

GNU/0x46494c45 with debugger context exits 1, code: capability_unavailable:

Core mapping note 1 cannot be confined to the owned debugger sysroot: missing mapping table header. Omit include_debugger_context for basic recorded evidence.

Its partial observation contains GNU/NT_FILE. Readelf instead reports Unknown note type: (0x46494c45). Changing only that number to 0x12345678 passes the mapping gate and fails later because GDB is unavailable. The genuine CORE/0x46494c45 control correctly receives the missing-mapping-header refusal.

Typed outcome or recovery expected

Return truthful owner-qualified note metadata. For debugger context, an opaque GNU note must pass the CORE mapping guard and reach debugger configuration checking; missing GDB may still produce its genuine configuration failure. Retain partial observations on genuine enrichment failures. These are proposed restoration checks, not results from a fix.

Analysis or suspected cause

Source-derived explanation: the pinned pyelftools 0.33 dependency chooses its note enum from ET_CORE without owner context. REA's recorded_crash_impl.py:110-130 emits note.n_type before its CORE-owner guard. RecordedCrashStructureBindings.ts:24-41,122-129 accepts the same owner-independent enum. CoreFileMappingAuthority.ts:10-32 selects mapping validation by symbolic type alone, and PwntoolsRecordedCrashProvider.ts:124-141 runs it before debugger configuration. This chain agrees with the CLI controls; the dependency's enum behavior does not establish that arbitrary owners carry CORE semantics.

Additional context

Prepared with AI assistance from an independently verified draft; the submitter inspected the retained CLI/readelf results and checked current source and duplicates. No fix or last-known-good version is established.

Related #4942 fixed confinement for actual CORE mapping paths; this report concerns GNU metadata incorrectly acquiring mapping semantics after that fix. #4914 concerns GDB executable auto-loading. Open PR #4837 adds a separate inspect-elf-notes operation and does not modify this recorded-crash decoder or mapping consumer. Those reports have different restoration checks.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5700 ### 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 `inspect-recorded-crash` assigns CORE note names to GNU-owned notes in an admitted Linux x86-64 core. GNU/type 3 becomes `NT_PRPSINFO` instead of GNU build-id metadata. A GNU-owned note with numeric type `0x46494c45` becomes `NT_FILE`, causing `--debugger-context` to reject it for a missing mapping-table header before debugger configuration is checked. An independent verifier reproduced both effects with the unmodified public CLI on Linux x64. Original owner/descriptor bytes remain available, including in the failure's partial observation. This is incorrect structured metadata and a false enrichment refusal; no security-boundary escape was demonstrated. ### Reproduction Run from the prepared source checkout on Linux x64 with Node.js, Python, procps-compatible `ps`, GNU `readelf`, and `jq` available. The caller-supplied Python profile below matches the documented dependencies. The three debugger-context commands intentionally exit nonzero; use a shell that continues after failure. ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" "$work_dir/state" "$work_dir/tmp" python3 -m venv "$work_dir/venv" "$work_dir/venv/bin/python" -m pip install pwntools==4.15.0 pyelftools==0.33 unicorn==2.1.2 python3 - "$work_dir" <<'PY' import pathlib, struct, sys out = pathlib.Path(sys.argv[1]) def note(owner, kind, descriptor): def padded(data): return data + bytes((-len(data)) % 4) return struct.pack('<III',len(owner),len(descriptor),kind) + padded(owner) + padded(descriptor) status = bytearray(336) struct.pack_into('<h',status,12,11) struct.pack_into('<i',status,32,123) struct.pack_into('<Q',status,240,0x401234) common = note(b'CORE\0',1,status) for name, owner, kind, desc in [ ('buildid',b'GNU\0',3,bytes(range(20))), ('mapping',b'GNU\0',0x46494c45,b''), ('unknown-control',b'GNU\0',0x12345678,b''), ('core-mapping-control',b'CORE\0',0x46494c45,b''), ('core-type3-control',b'CORE\0',3,bytes(136)), ]: notes = common + note(owner,kind,desc) ident = b'\x7fELF' + bytes([2,1,1,3,0]) + bytes(7) header = struct.pack('<16sHHIQQQIHHHHHH',ident,4,62,1,0,64,0,0,64,56,1,64,0,0) segment = struct.pack('<IIQQQQQQ',4,4,120,0,0,len(notes),len(notes),4) (out / (name+'.core')).write_bytes(header+segment+notes) PY inspect_core() { env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \ XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \ XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \ REA_PWNTOOLS_PYTHON="$work_dir/venv/bin/python" \ node scripts/rea.mjs inspect-recorded-crash "$@" --json } inspect_core "$work_dir/buildid.core" > "$work_dir/buildid.json" jq -c '.normalized_result.notes[] | {owner_display, type}' "$work_dir/buildid.json" inspect_core "$work_dir/core-type3-control.core" > "$work_dir/core-type3.json" jq -c '.normalized_result.notes[] | {owner_display, type}' "$work_dir/core-type3.json" inspect_core "$work_dir/mapping.core" --debugger-context inspect_core "$work_dir/unknown-control.core" --debugger-context inspect_core "$work_dir/core-mapping-control.core" --debugger-context readelf --notes "$work_dir/buildid.core" "$work_dir/mapping.core" ``` The verifier executed the equivalent isolated runner once per fixture at the baseline below. GDB and pwndbg were intentionally absent: the unknown-type control reaches their genuine configuration failure, while the colliding GNU note fails earlier at the mapping guard. No platform override or production-code change was used. ### Expected behavior Preserve owner-qualified note identity. GNU/type 3 should retain a GNU build-id name or numeric type 3. An unfamiliar GNU-owned type should remain opaque. Only a genuine CORE mapping note should receive Linux mapping-table validation. Preserve existing CORE/type-3 naming and genuine CORE mapping confinement checks. The [recorded-crash contract](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/recorded-crashes.md#L28-L29) promises original identity for unfamiliar note owners and kinds; the same guide documents this supported ELF profile. ### Impact and scope P2: supported inputs return misleading note identities, and one owner/type collision blocks the optional debugger-context facet. Basic decoded evidence, raw bytes, and the original recording remain available. Omitting debugger context or using readelf provides partial mitigation but does not restore the requested enrichment; an enrichment-preserving workaround is unverified. Real-producer prevalence, other owners/architectures, MCP execution, and successful full debugger enrichment were not tested. ### REA version `rea-agents` 6.7.0, source `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`, prepared compiled CLI. Submission recheck fetched `origin/main` at `52c84f6f58fa3cc7b39326654c6d3f261f957957`: none of the cited expected/failure source files changed since the baseline. This was a source freshness check, not another runtime replay on main. ### Runtime and platform Verified runtime: Node.js v24.18.0, Linux x64 in an explicitly selected linux/amd64 Docker container, Python 3.12, pwntools 4.15.0, pyelftools 0.33, Unicorn 2.1.2, GNU readelf 2.44, procps installed. Docker client/Engine 29.9.1; arm64 Engine host. Submission host checks: Node.js v26.12.0; macOS 27.0.1; arm64. The CLI reproduction ran inside Linux, not on that macOS host. ### Target and artifact context Synthetic bytes only: ELF64 little-endian x86-64 ET_CORE, GNU/Linux OSABI, ABI version zero, one PT_NOTE segment, a 336-byte CORE/NT_PRSTATUS descriptor and one varied owner/type note. No live target, PT_LOAD segment, real crash data, or target execution is needed. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The verified build-id invocation exits 0 and reports: ```json {"owner_display":"CORE","type":"NT_PRSTATUS"} {"owner_display":"GNU","type":"NT_PRPSINFO"} ``` GNU readelf identifies that same second note as `NT_GNU_BUILD_ID`, build ID `000102030405060708090a0b0c0d0e0f10111213`. The CORE/type-3 control exits 0 and correctly reports `NT_PRPSINFO`. The JSON note record has no separate numeric-type field, although the retained artifact/source range still permits reading the original note header. GNU/`0x46494c45` with debugger context exits 1, `code: capability_unavailable`: ```text Core mapping note 1 cannot be confined to the owned debugger sysroot: missing mapping table header. Omit include_debugger_context for basic recorded evidence. ``` Its partial observation contains GNU/`NT_FILE`. Readelf instead reports `Unknown note type: (0x46494c45)`. Changing only that number to `0x12345678` passes the mapping gate and fails later because GDB is unavailable. The genuine CORE/`0x46494c45` control correctly receives the missing-mapping-header refusal. ### Typed outcome or recovery expected Return truthful owner-qualified note metadata. For debugger context, an opaque GNU note must pass the CORE mapping guard and reach debugger configuration checking; missing GDB may still produce its genuine configuration failure. Retain partial observations on genuine enrichment failures. These are proposed restoration checks, not results from a fix. ### Analysis or suspected cause Source-derived explanation: the pinned **pyelftools 0.33** dependency chooses its note enum from ET_CORE without owner context. REA's [`recorded_crash_impl.py:110-130`](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/bridge/pwntools/recorded_crash_impl.py#L110-L130) emits `note.n_type` before its CORE-owner guard. [`RecordedCrashStructureBindings.ts:24-41,122-129`](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/pwntools/RecordedCrashStructureBindings.ts#L24-L41) accepts the same owner-independent enum. [`CoreFileMappingAuthority.ts:10-32`](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/pwndbg/CoreFileMappingAuthority.ts#L10-L32) selects mapping validation by symbolic type alone, and [`PwntoolsRecordedCrashProvider.ts:124-141`](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/pwntools/PwntoolsRecordedCrashProvider.ts#L124-L141) runs it before debugger configuration. This chain agrees with the CLI controls; the dependency's enum behavior does not establish that arbitrary owners carry CORE semantics. ### Additional context Prepared with AI assistance from an independently verified draft; the submitter inspected the retained CLI/readelf results and checked current source and duplicates. No fix or last-known-good version is established. Related #4942 fixed confinement for actual CORE mapping paths; this report concerns GNU metadata incorrectly acquiring mapping semantics after that fix. #4914 concerns GDB executable auto-loading. Open PR #4837 adds a separate `inspect-elf-notes` operation and does not modify this recorded-crash decoder or mapping consumer. Those reports have different restoration checks.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Verified resolved on main at d87e73f2db.

All five original note fixtures passed production Python decoding and basic public CLI inspection on Linux x64. The exact four-byte GNU\0 owner retains numeric types 3, 0x46494c45 and 0x12345678; genuine CORE\0 controls retain NT_PRPSINFO and NT_FILE. Raw type IDs, owner/descriptor bytes and artifact digests match the inputs. The production mapping validator accepts the GNU collisions and still refuses the incomplete genuine CORE mapping with its missing-header reason. No debugger was launched; debugger-context enrichment and MCP were outside this check.

The checks used pinned Node 24.22.1 and pwntools 4.15.0. Relevant implementation: recorded_crash_impl.py, recordedCrash.ts.

Closing as completed on main.

<!-- gh-comment-id:6380788491 --> @coygeek commented on GitHub (Oct 24, 2026): Verified resolved on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. All five original note fixtures passed production Python decoding and basic public CLI inspection on Linux x64. The exact four-byte `GNU\0` owner retains numeric types `3`, `0x46494c45` and `0x12345678`; genuine `CORE\0` controls retain `NT_PRPSINFO` and `NT_FILE`. Raw type IDs, owner/descriptor bytes and artifact digests match the inputs. The production mapping validator accepts the GNU collisions and still refuses the incomplete genuine CORE mapping with its missing-header reason. No debugger was launched; debugger-context enrichment and MCP were outside this check. The checks used pinned Node 24.22.1 and pwntools 4.15.0. Relevant implementation: [recorded_crash_impl.py](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/bridge/pwntools/recorded_crash_impl.py#L113), [recordedCrash.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/native/recordedCrash.ts#L19). Closing as completed on main.
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#3450
No description provided.