fix(native): bind core segment names to their architecture

Validate recorded-core program-header projections against the selected pyelftools architecture table after binding e_machine to the snapshot. Preserve numeric unknowns and reject names borrowed from another machine or inconsistent header bytes.

Add source-binding malformed controls and authored auxiliary-header carriers to the existing real CLI/MCP verification lane. Document pinned parser semantics separately from payload interpretation and real crash coverage.

Fixes #6538
This commit is contained in:
morluto
2026-10-24 04:54:47 +08:00
parent 1081f81484
commit 462b73ac9b
5 changed files with 162 additions and 6 deletions
+21
View File
@@ -5,6 +5,7 @@ import json
from pathlib import Path
import sys
from elftools.elf.elffile import ELFFile
from elftools.elf.structs import ELFStructs
from elftools.construct import Container
from pwnlib.elf.datatypes import elf_prstatus_amd64
@@ -31,6 +32,26 @@ def note_header(content, note, **changes):
content[note.n_offset:note.n_offset + len(encoded)] = encoded
# Authored metadata carriers exercise actual parser projection through public
# inspection, independently of the host that produced the captured amd64 core.
for name, machine, kind in [
("aarch64-archext", "EM_AARCH64", "PT_AARCH64_ARCHEXT"),
("aarch64-unwind", "EM_AARCH64", "PT_AARCH64_UNWIND"),
("aarch64-mte-opaque", "EM_AARCH64", 0x70000002),
("amd64-processor-opaque", "EM_X86_64", 0x70000001),
]:
structs = ELFStructs(little_endian=True, elfclass=64)
structs.create_basic_structs()
structs.create_advanced_structs("ET_CORE", machine, image.header.e_ident.EI_OSABI)
header_bytes, entry_bytes = structs.Elf_Ehdr.sizeof(), structs.Elf_Phdr.sizeof()
header = Container(**dict(image.header, e_machine=machine, e_entry=0, e_ehsize=header_bytes,
e_phoff=header_bytes, e_phentsize=entry_bytes, e_phnum=1,
e_shoff=0, e_shentsize=64, e_shnum=0, e_shstrndx=0, e_flags=0))
segment = Container(p_type=kind, p_flags=4, p_offset=header_bytes + entry_bytes,
p_vaddr=0, p_paddr=0, p_filesz=4, p_memsz=4, p_align=4)
save(name, structs.Elf_Ehdr.build(header) + structs.Elf_Phdr.build(segment) + bytes(4))
save("truncated-header", original[:32])
for name, osabi, version in [("linux-osabi", 3, 0), ("solaris-osabi", 6, 0), ("freebsd-osabi", 9, 0), ("unsupported-abi-version", 0, 1)]:
content = bytearray(original)
+16
View File
@@ -189,6 +189,22 @@ try {
assert.equal(noNotes.threads.length, 0);
assert.equal(noNotes.note_interpretation_completeness, "unknown");
cases++;
for (const [name, architecture, type] of [
["aarch64-archext", "aarch64-little-endian", "PT_AARCH64_ARCHEXT"],
["aarch64-unwind", "aarch64-little-endian", "PT_AARCH64_UNWIND"],
["aarch64-mte-opaque", "aarch64-little-endian", 0x70000002],
["amd64-processor-opaque", "x86_64-little-endian", 0x70000001],
]) {
const auxiliary = await basic.inspect(mode, fixtures[name]);
assert.equal(auxiliary.architecture, architecture);
assert.equal(auxiliary.segments.length, 1);
assert.equal(auxiliary.segments[0].type, type);
assert.equal(auxiliary.notes.length, 0);
assert.equal(auxiliary.threads.length, 0);
assert.equal(auxiliary.note_interpretation_completeness, "unknown");
assert.equal(auxiliary.live_process_identity, "unknown");
cases++;
}
const negative = await basic.inspect(mode, fixtures["negative-pid"]);
assert.ok(negative.threads.some((thread) => thread.historical_pid === -2));
cases++;