From 9b4deb2655ab471698daca8d5d8449fec29fcbb1 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:46:21 +0800 Subject: [PATCH] fix(core): bind numeric type identities and qualify note owners --- bridge/pwntools/recorded_crash_impl.py | 22 ++++--- docs/recorded-crashes.md | 8 +++ scripts/lib/recorded-core-fixtures.py | 13 ++++ scripts/verify-recorded-crash.mjs | 27 ++++++++ .../RecordedCrashService.test.ts | 1 + src/domain/native/recordedCrash.test.ts | 1 + src/domain/native/recordedCrash.ts | 65 ++++++++++++------- .../native/recordedCrashNoteCoverage.ts | 4 +- .../pwndbg/CoreFileMappingAuthority.test.ts | 9 +++ src/native/pwndbg/CoreFileMappingAuthority.ts | 7 +- .../RecordedCrashStructureBindings.test.ts | 52 +++++++++++++++ .../RecordedCrashStructureBindings.ts | 52 +-------------- .../recordedCrashProvider.test.ts | 2 +- .../binaryDiagnostics/recordedCrash.ts | 15 ++--- 14 files changed, 184 insertions(+), 94 deletions(-) create mode 100644 src/native/pwntools/RecordedCrashStructureBindings.test.ts diff --git a/bridge/pwntools/recorded_crash_impl.py b/bridge/pwntools/recorded_crash_impl.py index 789c7ca94b..0e9352ef4f 100644 --- a/bridge/pwntools/recorded_crash_impl.py +++ b/bridge/pwntools/recorded_crash_impl.py @@ -88,16 +88,18 @@ def inspect_core(path, cache): segments, notes, padding, threads, signals = [], [], [], [], [] for segment_index, segment in enumerate(image.iter_segments()): h = segment.header - backing = h.p_type != "PT_NULL" and h.p_filesz > 0 + header_offset = header.e_phoff + segment_index * header.e_phentsize + type_id = int.from_bytes(content[header_offset:header_offset + 4], "little") + backing = type_id != 0 and h.p_filesz > 0 if backing: location(h.p_offset, h.p_filesz, len(content)) - if h.p_type == "PT_LOAD" and h.p_filesz > h.p_memsz: + if type_id == 1 and h.p_filesz > h.p_memsz: raise CoreFailure("format", "Recorded PT_LOAD file bytes exceed memory size.") - segments.append({"index": segment_index, "type": h.p_type, - "header_location": location(header.e_phoff + segment_index * header.e_phentsize, header.e_phentsize, len(content)), + segments.append({"index": segment_index, "type": h.p_type, "type_id": type_id, + "header_location": location(header_offset, header.e_phentsize, len(content)), "offset": hex(h.p_offset), "file_size": hex(h.p_filesz), "memory_size": hex(h.p_memsz), "virtual_address": hex(h.p_vaddr), "physical_address": hex(h.p_paddr), "alignment": hex(h.p_align), "flags": hex(h.p_flags), "file_backing": "file" if backing else "none"}) - if h.p_type != "PT_NOTE" or h.p_filesz == 0: + if type_id != 4 or h.p_filesz == 0: continue position, end = h.p_offset, h.p_offset + h.p_filesz header_size = image.structs.Elf_Nhdr.sizeof() @@ -109,6 +111,7 @@ def inspect_core(path, cache): padding.append({"segment_index": segment_index, "location": location(position, len(raw), end), "bytes_base64": encoded(raw)}) break note = struct_parse(image.structs.Elf_Nhdr, image.stream, stream_pos=position) + note_type_id = int.from_bytes(content[position + 8:position + 12], "little") owner_start = position + header_size descriptor_start = owner_start + ((note.n_namesz + 3) // 4) * 4 note_end = descriptor_start + ((note.n_descsz + 3) // 4) * 4 @@ -119,7 +122,10 @@ def inspect_core(path, cache): display = owner.split(b"\0", 1)[0].decode("utf-8", "replace") if owner else None descriptor = content[descriptor_start:descriptor_start + note.n_descsz] note_index = len(notes) - notes.append({"index": note_index, "segment_index": segment_index, "type": note.n_type, + # pyelftools selects ET_CORE labels without owner context. Keep + # those labels only for exact CORE owners; other kinds are opaque. + note_type = note.n_type if owner == b"CORE\0" else note_type_id + notes.append({"index": note_index, "segment_index": segment_index, "type": note_type, "type_id": note_type_id, "location": location(position, note_end - position, end), "owner_location": location(owner_start, len(owner), end), "owner_bytes_base64": encoded(owner), "owner_display": display, "descriptor_location": location(descriptor_start, len(descriptor), end), "descriptor_bytes_base64": encoded(descriptor)}) @@ -127,7 +133,7 @@ def inspect_core(path, cache): # Only the initial Linux CORE ABI is interpreted. Other owners retain exact bytes. if owner != b"CORE\0": continue - if note.n_type == "NT_PRSTATUS": + if note_type_id == 1: if len(descriptor) < ctypes.sizeof(prstatus_type): raise CoreFailure("format", "Linux " + profile_label + " NT_PRSTATUS descriptor is truncated.") status = prstatus_type.from_buffer_copy(descriptor) @@ -136,7 +142,7 @@ def inspect_core(path, cache): relative = prstatus_type.pr_reg.offset + getattr(registers_type, name).offset registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)}) threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers}) - elif note.n_type == "NT_SIGINFO": + elif note_type_id == 0x53494749: if len(descriptor) < ctypes.sizeof(elf_siginfo_64): raise CoreFailure("format", "Linux " + profile_label + " NT_SIGINFO descriptor is truncated.") signal = elf_siginfo_64.from_buffer_copy(descriptor) diff --git a/docs/recorded-crashes.md b/docs/recorded-crashes.md index 7af17e7bbe..0eadabad81 100644 --- a/docs/recorded-crashes.md +++ b/docs/recorded-crashes.md @@ -25,8 +25,16 @@ note interpretation; an unspecified OSABI alone does not prove OS origin. are inline through CLI and MCP. - Original program headers and recorded segment file/memory ranges. Undumped memory and current executable/library identity remain unknown. +- Program headers and notes retain their original numeric `type_id` separately + from the decoder's `type` label. Source binding and semantic decisions use the + numeric identity; an architecture-specific display name does not establish + support for that segment's payload semantics. - Every note's original identity, owner/descriptor bytes and source ranges, including unfamiliar owners and note kinds. + CORE symbolic note labels apply only to the exact `CORE\0` owner. Other + owners keep numeric types, so GNU/type 3 remains opaque instead of acquiring + CORE process-info semantics, and a GNU-owned numeric NT_FILE collision does + not acquire debugger mapping authority. - Every exact Linux CORE PRSTATUS note as a separate recorded thread, with historical PID, signal and lossless uint64 registers bound to source bytes. Each supported note has exactly one interpretation; each thread contains the diff --git a/scripts/lib/recorded-core-fixtures.py b/scripts/lib/recorded-core-fixtures.py index ba65bb3cab..323096ce66 100644 --- a/scripts/lib/recorded-core-fixtures.py +++ b/scripts/lib/recorded-core-fixtures.py @@ -64,6 +64,19 @@ save("truncated-owner", content) content = bytearray(original) content[status.n_offset + image.structs.Elf_Nhdr.sizeof()] = 255 save("opaque-owner", content) +for name, kind in [("gnu-type3", 3), ("gnu-file-collision", 0x46494c45)]: + content = bytearray(original) + note_header(content, status, n_type=kind) + owner_start = status.n_offset + image.structs.Elf_Nhdr.sizeof() + # Preserve the original five-byte name extent and all note alignment. + content[owner_start:owner_start + 5] = b"GNU\0\0" + save(name, content) +for name, kind in [("aarch64-archext", 0x70000000), ("aarch64-unwind", 0x70000001)]: + header = Container(**dict(image.header, e_machine="EM_AARCH64", e_phoff=64, e_phentsize=56, + e_phnum=1, e_shoff=0, e_shnum=0, e_shstrndx=0)) + segment = Container(p_type=kind, p_flags=4, p_offset=120, p_vaddr=0, p_paddr=0, + p_filesz=4, p_memsz=4, p_align=4) + save(name, image.structs.Elf_Ehdr.build(header) + image.structs.Elf_Phdr.build(segment) + bytes(4)) content = bytearray(original) for note in notes: if note.n_type == "NT_SIGINFO": diff --git a/scripts/verify-recorded-crash.mjs b/scripts/verify-recorded-crash.mjs index c8eb29f1df..36bc8028d1 100644 --- a/scripts/verify-recorded-crash.mjs +++ b/scripts/verify-recorded-crash.mjs @@ -180,6 +180,33 @@ try { ); assert.equal(opaque.threads.length, 1); cases++; + for (const [name, kind] of [ + ["gnu-type3", 3], + ["gnu-file-collision", 0x46494c45], + ]) { + const value = await basic.inspect(mode, fixtures[name]); + assert.ok( + value.notes.some( + (note) => + note.owner_display === "GNU" && + note.type === kind && + note.type_id === kind, + ), + ); + assert.equal(value.threads.length, 1); + cases++; + } + for (const [name, kind, label] of [ + ["aarch64-archext", 0x70000000, "PT_AARCH64_ARCHEXT"], + ["aarch64-unwind", 0x70000001, "PT_AARCH64_UNWIND"], + ]) { + const value = await basic.inspect(mode, fixtures[name]); + assert.equal(value.architecture, "aarch64-little-endian"); + assert.equal(value.segments[0]?.type_id, kind); + assert.equal(value.segments[0]?.type, label); + assert.equal(value.threads.length, 0); + cases++; + } const noSignals = await basic.inspect(mode, fixtures["unknown-signals"]); assert.equal(noSignals.signals.length, 0); assert.equal(noSignals.threads.length, 2); diff --git a/src/application/binaryDiagnostics/RecordedCrashService.test.ts b/src/application/binaryDiagnostics/RecordedCrashService.test.ts index 53b55fbbaa..82fe247adb 100644 --- a/src/application/binaryDiagnostics/RecordedCrashService.test.ts +++ b/src/application/binaryDiagnostics/RecordedCrashService.test.ts @@ -195,6 +195,7 @@ it.each([ fixture.segments.push({ index: 0, type: "PT_LOAD", + type_id: 1, offset: "0x200", header_location: { offset: "0x0", bytes: "0x38" }, file_backing: "file", diff --git a/src/domain/native/recordedCrash.test.ts b/src/domain/native/recordedCrash.test.ts index 18414128be..40258797d1 100644 --- a/src/domain/native/recordedCrash.test.ts +++ b/src/domain/native/recordedCrash.test.ts @@ -208,6 +208,7 @@ it("does not interpret a signal through a mislabeled raw owner ABI", () => { notes: value.notes.map((note) => ({ ...note, type: "NT_SIGINFO", + type_id: 0x53494749, owner_bytes_base64: Buffer.from("EVIL\0").toString("base64"), })), signals: [ diff --git a/src/domain/native/recordedCrash.ts b/src/domain/native/recordedCrash.ts index e722dc891b..f28a00a3b9 100644 --- a/src/domain/native/recordedCrash.ts +++ b/src/domain/native/recordedCrash.ts @@ -19,6 +19,15 @@ const diagnostics = z.strictObject({ // Exact b"CORE\0"; a lossy display label cannot establish the Linux note ABI. const LINUX_CORE_OWNER_BASE64 = "Q09SRQA="; +/** Source identity controls interpretation; decoder labels are display facts. */ +export const isLinuxCoreNote = ( + note: + | { readonly owner_bytes_base64: string; readonly type_id: number } + | undefined, + type: number, +): boolean => + note?.owner_bytes_base64 === LINUX_CORE_OWNER_BASE64 && note.type_id === type; + /** Complete register identities in the supported Linux amd64 PRSTATUS ABI order. */ const RECORDED_LINUX_AMD64_REGISTERS: readonly string[] = [ "r15", @@ -206,6 +215,7 @@ const recordedCrashObjectSchema = z.strictObject({ z.strictObject({ index, type: z.union([z.string(), index]), + type_id: index.max(0xffffffff), header_location: range, offset: hex, file_size: hex, @@ -222,6 +232,7 @@ const recordedCrashObjectSchema = z.strictObject({ index, segment_index: index, type: z.union([z.string(), index]), + type_id: index.max(0xffffffff), location: range, owner_location: range, owner_bytes_base64: base64, @@ -298,6 +309,8 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( )) fail(issue); for (const [position, segment] of value.segments.entries()) { + if (typeof segment.type === "number" && segment.type !== segment.type_id) + fail("Numeric segment type differs from its source identity."); if (position !== segment.index || !within(segment.header_location)) fail("Invalid original segment identity or header range."); if ( @@ -306,20 +319,39 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( ) fail("Recorded segment exceeds artifact bytes."); if ( - segment.type === "PT_LOAD" && + segment.type_id === 1 && BigInt(segment.file_size) > BigInt(segment.memory_size) ) fail("Recorded PT_LOAD file size exceeds memory size."); } for (const [position, note] of value.notes.entries()) { const segment = value.segments[note.segment_index]; - if (position !== note.index || segment?.type !== "PT_NOTE") { + if (position !== note.index || segment?.type_id !== 4) { fail("Invalid original note identity or segment reference."); continue; } + const ownerBytes = Buffer.from(note.owner_bytes_base64, "base64"); + const terminator = ownerBytes.indexOf(0); + const ownerDisplay = + ownerBytes.length === 0 + ? null + : ownerBytes.subarray(0, terminator).toString("utf8"); if ( - BigInt(Buffer.byteLength(note.owner_bytes_base64, "base64")) !== - BigInt(note.owner_location.bytes) || + (ownerBytes.length > 0 && terminator < 0) || + note.owner_display !== ownerDisplay + ) + fail("Note owner display differs from its retained bytes."); + if (typeof note.type === "number" && note.type !== note.type_id) + fail("Numeric note type differs from its source identity."); + if ( + note.owner_bytes_base64 !== LINUX_CORE_OWNER_BASE64 && + note.type !== note.type_id + ) + fail( + "Uninterpreted note owners must retain their numeric type identity.", + ); + if ( + BigInt(ownerBytes.length) !== BigInt(note.owner_location.bytes) || BigInt(Buffer.byteLength(note.descriptor_bytes_base64, "base64")) !== BigInt(note.descriptor_location.bytes) ) @@ -344,7 +376,7 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( for (const padding of value.note_padding) { const segment = value.segments[padding.segment_index]; if ( - segment?.type !== "PT_NOTE" || + segment?.type_id !== 4 || !within( padding.location, BigInt(segment.offset), @@ -354,13 +386,11 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( fail("Invalid recorded note padding range."); } for (const [kind, rows] of [ - ["NT_PRSTATUS", value.threads], - ["NT_SIGINFO", value.signals], + [1, value.threads], + [0x53494749, value.signals], ] as const) { - const matchingNotes = value.notes.filter( - (note) => - note.type === kind && - note.owner_bytes_base64 === LINUX_CORE_OWNER_BASE64, + const matchingNotes = value.notes.filter((note) => + isLinuxCoreNote(note, kind), ); if ( rows.length !== matchingNotes.length || @@ -374,11 +404,7 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( const profileRegisters = recordedCoreRegisterSets[value.architecture]; for (const thread of value.threads) { const note = value.notes[thread.note_index]; - if ( - note?.type !== "NT_PRSTATUS" || - note.owner_display !== "CORE" || - note.owner_bytes_base64 !== LINUX_CORE_OWNER_BASE64 - ) { + if (note === undefined || !isLinuxCoreNote(note, 1)) { fail("Thread references a different note kind."); continue; } @@ -404,12 +430,7 @@ export const recordedCrashSchema = recordedCrashObjectSchema.superRefine( fail("Register source exceeds its descriptor."); } for (const signal of value.signals) { - if ( - value.notes[signal.note_index]?.type !== "NT_SIGINFO" || - value.notes[signal.note_index]?.owner_display !== "CORE" || - value.notes[signal.note_index]?.owner_bytes_base64 !== - LINUX_CORE_OWNER_BASE64 - ) + if (!isLinuxCoreNote(value.notes[signal.note_index], 0x53494749)) fail("Signal references a different note kind."); const known = signal.number === 11 && (signal.code === 1 || signal.code === 2); diff --git a/src/domain/native/recordedCrashNoteCoverage.ts b/src/domain/native/recordedCrashNoteCoverage.ts index 4eb28a84fa..5881e308e7 100644 --- a/src/domain/native/recordedCrashNoteCoverage.ts +++ b/src/domain/native/recordedCrashNoteCoverage.ts @@ -1,7 +1,7 @@ type Range = { readonly offset: string; readonly bytes: string }; type Segment = { readonly index: number; - readonly type: string | number; + readonly type_id: number; readonly offset: string; readonly file_size: string; }; @@ -87,7 +87,7 @@ export function recordedNoteCoverageIssues( if (start + BigInt(range.bytes) > end) end = start + BigInt(range.bytes); } for (const segment of segments) { - if (segment.type !== "PT_NOTE") continue; + if (segment.type_id !== 4) continue; let cursor = BigInt(segment.offset); for (const range of (bySegment.get(segment.index) ?? []).sort(compare)) { if (BigInt(range.offset) !== cursor) diff --git a/src/native/pwndbg/CoreFileMappingAuthority.test.ts b/src/native/pwndbg/CoreFileMappingAuthority.test.ts index 5765632645..25dc0bae61 100644 --- a/src/native/pwndbg/CoreFileMappingAuthority.test.ts +++ b/src/native/pwndbg/CoreFileMappingAuthority.test.ts @@ -15,6 +15,15 @@ function mapping(paths: readonly Buffer[]) { } describe("core-only NT_FILE authority", () => { + it("keeps GNU-owned colliding note numbers outside CORE mapping authority", () => { + const { report, bytes } = mapping([Buffer.from("../unsafe")]); + expect(validateCoreFileMappingAuthority(report, bytes).ok).toBe(false); + report.notes[0]!.owner_bytes_base64 = + Buffer.from("GNU\0\0").toString("base64"); + report.notes[0]!.owner_display = "GNU"; + report.notes[0]!.type = 0x46494c45; + expect(validateCoreFileMappingAuthority(report, bytes).ok).toBe(true); + }); it.each([ "/usr/bin/app", "//usr/lib/libc.so", diff --git a/src/native/pwndbg/CoreFileMappingAuthority.ts b/src/native/pwndbg/CoreFileMappingAuthority.ts index 6a9d298b91..ec6bd34db3 100644 --- a/src/native/pwndbg/CoreFileMappingAuthority.ts +++ b/src/native/pwndbg/CoreFileMappingAuthority.ts @@ -1,5 +1,8 @@ import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js"; -import type { RecordedCrash } from "../../domain/native/recordedCrash.js"; +import { + isLinuxCoreNote, + type RecordedCrash, +} from "../../domain/native/recordedCrash.js"; import { err, ok, type Result } from "../../domain/result.js"; /** Interpret source-bound ELF64 NT_FILE paths once, before GDB can open them. */ @@ -8,7 +11,7 @@ export function validateCoreFileMappingAuthority( snapshot: Buffer, ): Result { for (const note of report.notes) { - if (note.type !== "NT_FILE") continue; + if (!isLinuxCoreNote(note, 0x46494c45)) continue; const refuse = (reason: string) => { const message = `Core mapping note ${note.index} cannot be confined to the owned debugger sysroot: ${reason}.`; return err( diff --git a/src/native/pwntools/RecordedCrashStructureBindings.test.ts b/src/native/pwntools/RecordedCrashStructureBindings.test.ts new file mode 100644 index 0000000000..bd7cceae00 --- /dev/null +++ b/src/native/pwntools/RecordedCrashStructureBindings.test.ts @@ -0,0 +1,52 @@ +import { expect, it } from "vitest"; +import { recordedCrashSchema } from "../../domain/native/recordedCrash.js"; +import { + recordedCrashFixture, + recordedCrashFixtureBytes, +} from "../../../tests/fixtures/binaryDiagnostics/recordedCrash.js"; +import { validateRecordedCrashStructure } from "./RecordedCrashStructureBindings.js"; + +it.each([ + ["aarch64-little-endian", 0x70000000, "PT_AARCH64_ARCHEXT"], + ["aarch64-little-endian", 0x70000001, "PT_AARCH64_UNWIND"], + ["aarch64-little-endian", 0x70000003, 0x70000003], + ["x86_64-little-endian", 0x70000001, 0x70000001], +] as const)( + "binds %s header type %i independently of its decoder label %s", + (architecture, typeId, label) => { + const report = recordedCrashFixture(undefined, architecture); + report.segments[0]!.type_id = typeId; + report.segments[0]!.type = label; + report.notes = []; + report.threads = []; + const bytes = recordedCrashFixtureBytes(report); + const parsed = recordedCrashSchema.parse(report); + expect(() => + validateRecordedCrashStructure(parsed, bytes, parsed.decoder_diagnostics), + ).not.toThrow(); + bytes.writeUInt32LE(typeId + 1, 64); + expect(() => + validateRecordedCrashStructure(parsed, bytes, parsed.decoder_diagnostics), + ).toThrow(/original ELF\/note headers/); + }, +); + +it.each([3, 0x46494c45])( + "retains GNU-owned numeric note %i without CORE interpretation", + (typeId) => { + const report = recordedCrashFixture(); + report.notes[0]!.type_id = typeId; + report.notes[0]!.type = typeId; + report.notes[0]!.owner_bytes_base64 = + Buffer.from("GNU\0\0").toString("base64"); + report.notes[0]!.owner_display = "GNU"; + report.threads = []; + const bytes = recordedCrashFixtureBytes(report); + const parsed = recordedCrashSchema.parse(report); + expect(() => + validateRecordedCrashStructure(parsed, bytes, parsed.decoder_diagnostics), + ).not.toThrow(); + report.notes[0]!.type = typeId === 3 ? "NT_PRPSINFO" : "NT_FILE"; + expect(recordedCrashSchema.safeParse(report).success).toBe(false); + }, +); diff --git a/src/native/pwntools/RecordedCrashStructureBindings.ts b/src/native/pwntools/RecordedCrashStructureBindings.ts index c1e2790b20..b94002f3cd 100644 --- a/src/native/pwntools/RecordedCrashStructureBindings.ts +++ b/src/native/pwntools/RecordedCrashStructureBindings.ts @@ -4,50 +4,13 @@ import { type RecordedCrash, } from "../../domain/native/recordedCrash.js"; -// Exact amd64/core enum projections in the pinned pyelftools 0.33 profile. -const segmentKinds = new Map([ - [0, "PT_NULL"], - [1, "PT_LOAD"], - [2, "PT_DYNAMIC"], - [3, "PT_INTERP"], - [4, "PT_NOTE"], - [5, "PT_SHLIB"], - [6, "PT_PHDR"], - [7, "PT_TLS"], - [0x60000000, "PT_LOOS"], - [0x6fffffff, "PT_HIOS"], - [0x6474e550, "PT_GNU_EH_FRAME"], - [0x6474e551, "PT_GNU_STACK"], - [0x6474e552, "PT_GNU_RELRO"], - [0x6474e553, "PT_GNU_PROPERTY"], -]); -const noteKinds = new Map([ - [1, "NT_PRSTATUS"], - [2, "NT_FPREGSET"], - [3, "NT_PRPSINFO"], - [4, "NT_TASKSTRUCT"], - [6, "NT_AUXV"], - [0x53494749, "NT_SIGINFO"], - [0x46494c45, "NT_FILE"], -]); -const matchesKind = ( - raw: number, - reported: string | number, - kinds: ReadonlyMap, -): boolean => { - const known = kinds.get(raw); - return typeof reported === "string" - ? reported === known - : known === undefined && reported === raw; -}; - const segmentMatches = ( segment: RecordedCrash["segments"][number], raw: Buffer, ): boolean => { const type = raw.readUInt32LE(0); return ( - matchesKind(type, segment.type, segmentKinds) && + type === segment.type_id && BigInt(segment.flags) === BigInt(raw.readUInt32LE(4)) && segment.file_backing === (type === 0 || raw.readBigUInt64LE(32) === 0n ? "none" : "file") && @@ -122,19 +85,8 @@ export function validateRecordedCrashStructure( if ( BigInt(note.owner_location.bytes) !== BigInt(raw.readUInt32LE(0)) || BigInt(note.descriptor_location.bytes) !== BigInt(raw.readUInt32LE(4)) || - !matchesKind(raw.readUInt32LE(8), note.type, noteKinds) + raw.readUInt32LE(8) !== note.type_id ) fail(); - const owner = bytesAt( - BigInt(note.owner_location.offset), - raw.readUInt32LE(0), - ); - const terminator = owner.indexOf(0); - const display = - owner.length === 0 - ? null - : owner.subarray(0, terminator).toString("utf8"); - if ((owner.length > 0 && terminator < 0) || note.owner_display !== display) - fail(); } } diff --git a/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts b/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts index 5f321e7333..46343156b4 100644 --- a/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts +++ b/tests/boundary/binaryDiagnostics/recordedCrashProvider.test.ts @@ -609,7 +609,7 @@ const alterRecordedCrashReply = ( })); if (scenario === "wrong-note-kind") { payload.notes = payload.notes.map((note) => - note.index === 0 ? { ...note, type: "NT_AUXV" } : note, + note.index === 0 ? { ...note, type: "NT_AUXV", type_id: 6 } : note, ); payload.threads = []; } diff --git a/tests/fixtures/binaryDiagnostics/recordedCrash.ts b/tests/fixtures/binaryDiagnostics/recordedCrash.ts index 523b0f6342..2772a4c5b1 100644 --- a/tests/fixtures/binaryDiagnostics/recordedCrash.ts +++ b/tests/fixtures/binaryDiagnostics/recordedCrash.ts @@ -42,6 +42,7 @@ export const recordedCrashFixture = ( { index: 0, type: "PT_NOTE", + type_id: 4, header_location: { offset: "0x40", bytes: "0x38" }, offset: "0x80", file_size: hex(noteBytes), @@ -58,6 +59,7 @@ export const recordedCrashFixture = ( index: 0, segment_index: 0, type: "NT_PRSTATUS", + type_id: 1, location: { offset: hex(NOTE_START), bytes: hex(noteBytes) }, owner_location: { offset: hex(OWNER_START), bytes: "0x5" }, owner_bytes_base64: Buffer.from("CORE\0").toString("base64"), @@ -127,6 +129,7 @@ export const recordedCrashSignalFixture = ( index: 1, segment_index: 0, type: "NT_SIGINFO", + type_id: 0x53494749, location: { offset: hex(signalNoteStart), bytes: hex(signalNoteBytes) }, owner_location: { offset: hex(signalNoteStart + 12), bytes: "0x5" }, owner_bytes_base64: Buffer.from("CORE\0").toString("base64"), @@ -170,6 +173,7 @@ export const recordedCrashFileMappingFixture = ( { ...note, type: "NT_FILE", + type_id: 0x46494c45, location: { offset: hex(NOTE_START), bytes: hex(bytes) }, descriptor_location: { offset: hex(DESCRIPTOR_START), @@ -202,7 +206,7 @@ export const recordedCrashFixtureBytes = (value: RecordedCrash): Buffer => { bytes.writeUInt16LE(value.segments.length, 56); for (const segment of value.segments) { const start = Number(BigInt(segment.header_location.offset)); - bytes.writeUInt32LE(segment.type === "PT_NOTE" ? 4 : 0, start); + bytes.writeUInt32LE(segment.type_id, start); bytes.writeUInt32LE(Number(BigInt(segment.flags)), start + 4); for (const [field, offset] of [ [segment.offset, 8], @@ -221,14 +225,7 @@ export const recordedCrashFixtureBytes = (value: RecordedCrash): Buffer => { Number(BigInt(note.descriptor_location.bytes)), start + 4, ); - bytes.writeUInt32LE( - note.type === "NT_PRSTATUS" - ? 1 - : note.type === "NT_FILE" - ? 0x46494c45 - : 0x53494749, - start + 8, - ); + bytes.writeUInt32LE(note.type_id, start + 8); Buffer.from(note.owner_bytes_base64, "base64").copy( bytes, Number(BigInt(note.owner_location.offset)),