fix(elf): reject unmatched NT_FILE pathnames (#10138)

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Nianze Wuandmorluto authored and GitHub committed 2026-11-12 07:43:09 +08:00
1 parent 6b2f991808
commit fadfdf13e6
3 files changed
+67

No files matched your search

+3
View File
@@ -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
+57
View File
@@ -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
+7
View File
@@ -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;