From fadfdf13e617fa9a6025b6180459a13844f2a9ca Mon Sep 17 00:00:00 2001 From: Nianze Wu Date: Thu, 12 Nov 2026 07:43:09 +0800 Subject: [PATCH] fix(elf): reject unmatched NT_FILE pathnames (#10138) Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> --- docs/elf-notes.md | 3 ++ src/native/elf/ElfNoteParser.test.ts | 57 ++++++++++++++++++++++++++++ src/native/elf/ElfNoteParser.ts | 7 ++++ 3 files changed, 67 insertions(+) diff --git a/docs/elf-notes.md b/docs/elf-notes.md index 3e44ad1ba5..95e28b775d 100644 --- a/docs/elf-notes.md +++ b/docs/elf-notes.md @@ -44,6 +44,9 @@ file memory regions and paths (`NT_FILE`), fault signal and code (`NT_SIGINFO`), auxiliary vectors (`NT_AUXV`) across 32-bit and 64-bit images. Other owners or unparsed notes keep `core` null. +An `NT_FILE` descriptor with nonzero bytes after its declared filenames +retains its raw note evidence and leaves `core` null. + Linux `CORE` decoding requires System V/unspecified (0) or GNU/Linux (3) OSABI with ABI version zero. An unspecified OSABI alone does not establish Linux origin. An explicitly different OSABI or ABI version leaves `core` null diff --git a/src/native/elf/ElfNoteParser.test.ts b/src/native/elf/ElfNoteParser.test.ts index c663ac62d3..40f8cf0202 100644 --- a/src/native/elf/ElfNoteParser.test.ts +++ b/src/native/elf/ElfNoteParser.test.ts @@ -1079,6 +1079,63 @@ it("rejects 64-bit auxv tags and out-of-range page sizes with core: null without expect(report.entries[1]?.core).toBeNull(); }); +it.each([32, 64] as const)( + "leaves ELF%d NT_FILE mappings unknown when the descriptor contains a surplus pathname", + (bits) => { + const little = bits === 64; + const file = coreFileNote( + [{ start: 0x1000, end: 0x2000, file_ofs: 2, path: "/bin/app" }], + 4096, + bits, + little, + ); + const descriptorBytes = little + ? file.readUInt32LE(4) + : file.readUInt32BE(4); + // A five-byte CORE owner including its NUL pads to eight bytes. + const descriptor = file.subarray(20, 20 + descriptorBytes); + const inspect = (tail: Buffer) => { + const payload = Buffer.concat([descriptor, tail]); + return parse( + elfNoteFixture({ + bits, + little, + machine: little ? 62 : 20, + type: 4, + ownership: "both", + notes: elfNote("CORE", 0x46494c45, payload, little), + }), + ); + }; + const exact = inspect(Buffer.alloc(0)); + const padded = inspect(Buffer.alloc(3)); + expect(exact.entries).toHaveLength(2); + expect(padded.entries.map(({ core }) => core)).toEqual( + exact.entries.map(({ core }) => core), + ); + for (const entry of exact.entries) + expect(entry.core?.file?.mappings[0]).toMatchObject({ + start_address: "0x1000", + end_address: "0x2000", + file_offset_pages: "0x2", + path: "/bin/app", + }); + + const tail = Buffer.from("/lib/extra.so\0"); + const malformed = inspect(tail); + expect(malformed.coverage).toEqual(exact.coverage); + expect(malformed.entries).toHaveLength(2); + const payload = Buffer.concat([descriptor, tail]); + const sha256 = createHash("sha256").update(payload).digest("hex"); + for (const entry of malformed.entries) + expect(entry).toMatchObject({ + type_name: "core_file", + core: null, + descriptor: { bytes: payload.length, sha256 }, + }); + }, +); + it("does not spend text budget on malformed or discarded NT_FILE paths", () => { const header = Buffer.alloc(16); header.writeBigUInt64LE(2n, 0); // count = 2 diff --git a/src/native/elf/ElfNoteParser.ts b/src/native/elf/ElfNoteParser.ts index 7cda2f4dfe..35464a9005 100644 --- a/src/native/elf/ElfNoteParser.ts +++ b/src/native/elf/ElfNoteParser.ts @@ -450,6 +450,13 @@ const decodeCoreFile = ( totalPathBytes += nullIdx - strPos; strPos = nullIdx + 1; } + if ( + cachedScan === undefined && + descriptor.subarray(strPos).some((byte) => byte !== 0) + ) { + text.scans.set(scanKey, -1); + return null; + } text.scans.set(scanKey, count); const pathBytes = totalPathBytes * 3;