mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
refactor(elf): centralize raw table declarations
This commit is contained in:
1 parent
c994870f6b
commit
a7d71f47ee
11 files changed
+226
-90
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "../domain/elfRawTableDeclarations.js";
|
||||||
import type { FileHandle } from "node:fs/promises";
|
import type { FileHandle } from "node:fs/promises";
|
||||||
import type {
|
import type {
|
||||||
MipsAbiFlags,
|
MipsAbiFlags,
|
||||||
@@ -91,12 +92,15 @@ export const readMipsElfMetadata = async (
|
|||||||
if (fixedHeader.value.headerSize !== 52)
|
if (fixedHeader.value.headerSize !== 52)
|
||||||
return err("invalid MIPS ELF32 version or header size");
|
return err("invalid MIPS ELF32 version or header size");
|
||||||
|
|
||||||
|
const raw = readElfRawTableDeclarations(bytes, fixedHeader.value);
|
||||||
|
if (!raw.ok) return err(raw.error.message);
|
||||||
|
const { programs, sections } = raw.value;
|
||||||
const tables = [
|
const tables = [
|
||||||
{
|
{
|
||||||
label: "program header table",
|
label: "program header table",
|
||||||
offset: u32(bytes, 28),
|
offset: Number(programs.offset),
|
||||||
count: u16(bytes, 44),
|
count: programs.count,
|
||||||
stride: u16(bytes, 42),
|
stride: programs.width,
|
||||||
entrySize: 32,
|
entrySize: 32,
|
||||||
typeOffset: 0,
|
typeOffset: 0,
|
||||||
dataOffset: 4,
|
dataOffset: 4,
|
||||||
@@ -106,9 +110,9 @@ export const readMipsElfMetadata = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "section header table",
|
label: "section header table",
|
||||||
offset: u32(bytes, 32),
|
offset: Number(sections.offset),
|
||||||
count: u16(bytes, 48),
|
count: sections.count,
|
||||||
stride: u16(bytes, 46),
|
stride: sections.width,
|
||||||
entrySize: 40,
|
entrySize: 40,
|
||||||
typeOffset: 4,
|
typeOffset: 4,
|
||||||
dataOffset: 16,
|
dataOffset: 16,
|
||||||
@@ -119,7 +123,7 @@ export const readMipsElfMetadata = async (
|
|||||||
];
|
];
|
||||||
// Extended numbering needs a separate section-zero reader. Refuse it rather
|
// Extended numbering needs a separate section-zero reader. Refuse it rather
|
||||||
// than silently treating an unexamined table as having no ABI declarations.
|
// than silently treating an unexamined table as having no ABI declarations.
|
||||||
if (u16(bytes, 44) === 0xffff || u16(bytes, 48) >= 0xff00)
|
if (programs.count === 0xffff || sections.count >= 0xff00)
|
||||||
return err("extended or reserved MIPS ELF table numbering is unsupported");
|
return err("extended or reserved MIPS ELF table numbering is unsupported");
|
||||||
let record: Buffer | undefined;
|
let record: Buffer | undefined;
|
||||||
let sharedLoad: MipsElfMetadata["sharedLoad"];
|
let sharedLoad: MipsElfMetadata["sharedLoad"];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "../../domain/elfRawTableDeclarations.js";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { parsePeResources } from "../../native/pe/PeResourceParser.js";
|
import { parsePeResources } from "../../native/pe/PeResourceParser.js";
|
||||||
import {
|
import {
|
||||||
@@ -323,16 +324,22 @@ const locateElfNotes = async (
|
|||||||
Math.min(carrier.fileSize, header.value.headerSize),
|
Math.min(carrier.fileSize, header.value.headerSize),
|
||||||
);
|
);
|
||||||
if (fields === undefined) return [];
|
if (fields === undefined) return [];
|
||||||
const phoff = elf64
|
const raw = readElfRawTableDeclarations(fields, header.value);
|
||||||
? readU64(fields, 32, little)
|
if (!raw.ok) return [];
|
||||||
: readU32(fields, 28, little);
|
const { programs: programDeclarations, sections: sectionDeclarations } =
|
||||||
const shoff = elf64
|
raw.value;
|
||||||
? readU64(fields, 40, little)
|
const phoff =
|
||||||
: readU32(fields, 32, little);
|
programDeclarations.offset <= BigInt(Number.MAX_SAFE_INTEGER)
|
||||||
const phentsize = readU16(fields, elf64 ? 54 : 42, little);
|
? Number(programDeclarations.offset)
|
||||||
const phnum = readU16(fields, elf64 ? 56 : 44, little);
|
: undefined;
|
||||||
const shentsize = readU16(fields, elf64 ? 58 : 46, little);
|
const shoff =
|
||||||
const shnum = readU16(fields, elf64 ? 60 : 48, little);
|
sectionDeclarations.offset <= BigInt(Number.MAX_SAFE_INTEGER)
|
||||||
|
? Number(sectionDeclarations.offset)
|
||||||
|
: undefined;
|
||||||
|
const phentsize = programDeclarations.width;
|
||||||
|
const phnum = programDeclarations.count;
|
||||||
|
const shentsize = sectionDeclarations.width;
|
||||||
|
const shnum = sectionDeclarations.count;
|
||||||
const minimumProgram = elf64 ? 56 : 32;
|
const minimumProgram = elf64 ? 56 : 32;
|
||||||
const minimumSection = elf64 ? 64 : 40;
|
const minimumSection = elf64 ? 64 : 40;
|
||||||
const found: BlobRange[] = [];
|
const found: BlobRange[] = [];
|
||||||
@@ -645,9 +652,6 @@ const cString = (bytes: Buffer, offset: number, length: number): string => {
|
|||||||
return bytes.toString("latin1", offset, end);
|
return bytes.toString("latin1", offset, end);
|
||||||
};
|
};
|
||||||
|
|
||||||
const readU16 = (bytes: Buffer, offset: number, little: boolean): number =>
|
|
||||||
little ? bytes.readUInt16LE(offset) : bytes.readUInt16BE(offset);
|
|
||||||
|
|
||||||
const readU32 = (bytes: Buffer, offset: number, little: boolean): number =>
|
const readU32 = (bytes: Buffer, offset: number, little: boolean): number =>
|
||||||
little ? bytes.readUInt32LE(offset) : bytes.readUInt32BE(offset);
|
little ? bytes.readUInt32LE(offset) : bytes.readUInt32BE(offset);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "./elfRawTableDeclarations.js";
|
||||||
import { readElfFixedHeader, type ElfFixedHeader } from "./elfFixedHeader.js";
|
import { readElfFixedHeader, type ElfFixedHeader } from "./elfFixedHeader.js";
|
||||||
import { err, ok, type Result } from "./result.js";
|
import { err, ok, type Result } from "./result.js";
|
||||||
|
|
||||||
@@ -21,8 +22,6 @@ export interface ElfTableDeclarations {
|
|||||||
|
|
||||||
const reader = (bytes: Buffer, header: ElfFixedHeader) => {
|
const reader = (bytes: Buffer, header: ElfFixedHeader) => {
|
||||||
const little = header.byteOrder === "little";
|
const little = header.byteOrder === "little";
|
||||||
const u16 = (at: number) =>
|
|
||||||
little ? bytes.readUInt16LE(at) : bytes.readUInt16BE(at);
|
|
||||||
const u32 = (at: number) =>
|
const u32 = (at: number) =>
|
||||||
little ? bytes.readUInt32LE(at) : bytes.readUInt32BE(at);
|
little ? bytes.readUInt32LE(at) : bytes.readUInt32BE(at);
|
||||||
const word = (at: number) =>
|
const word = (at: number) =>
|
||||||
@@ -31,7 +30,7 @@ const reader = (bytes: Buffer, header: ElfFixedHeader) => {
|
|||||||
: little
|
: little
|
||||||
? bytes.readBigUInt64LE(at)
|
? bytes.readBigUInt64LE(at)
|
||||||
: bytes.readBigUInt64BE(at);
|
: bytes.readBigUInt64BE(at);
|
||||||
return { u16, u32, word };
|
return { u32, word };
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Zero-sized/inactive declarations own no file bytes. Arithmetic stays exact. */
|
/** Zero-sized/inactive declarations own no file bytes. Arithmetic stays exact. */
|
||||||
@@ -110,29 +109,6 @@ const extendedCounts = (
|
|||||||
return ok(counts);
|
return ok(counts);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TABLE_FIELDS = {
|
|
||||||
32: {
|
|
||||||
programOffset: 28,
|
|
||||||
sectionOffset: 32,
|
|
||||||
programWidth: 42,
|
|
||||||
programCount: 44,
|
|
||||||
sectionWidth: 46,
|
|
||||||
sectionCount: 48,
|
|
||||||
namesIndex: 50,
|
|
||||||
sectionHeaderSize: 40,
|
|
||||||
},
|
|
||||||
64: {
|
|
||||||
programOffset: 32,
|
|
||||||
sectionOffset: 40,
|
|
||||||
programWidth: 54,
|
|
||||||
programCount: 56,
|
|
||||||
sectionWidth: 58,
|
|
||||||
sectionCount: 60,
|
|
||||||
namesIndex: 62,
|
|
||||||
sectionHeaderSize: 64,
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** Canonical table declarations, including extended counts in inactive section zero. */
|
/** Canonical table declarations, including extended counts in inactive section zero. */
|
||||||
export function* readElfTableDeclarations(
|
export function* readElfTableDeclarations(
|
||||||
bytes: Buffer,
|
bytes: Buffer,
|
||||||
@@ -141,15 +117,16 @@ export function* readElfTableDeclarations(
|
|||||||
const fixed = readElfFixedHeader(bytes, fileSize);
|
const fixed = readElfFixedHeader(bytes, fileSize);
|
||||||
if (!fixed.ok) return fixed;
|
if (!fixed.ok) return fixed;
|
||||||
const header = fixed.value;
|
const header = fixed.value;
|
||||||
const r = reader(bytes, header);
|
const raw = readElfRawTableDeclarations(bytes, header);
|
||||||
const fields = TABLE_FIELDS[header.elfClass];
|
if (!raw.ok) return err(raw.error.message);
|
||||||
const programOffset = r.word(fields.programOffset);
|
const { programs, sections } = raw.value;
|
||||||
const sectionOffset = r.word(fields.sectionOffset);
|
const programOffset = programs.offset;
|
||||||
const programWidth = r.u16(fields.programWidth);
|
const sectionOffset = sections.offset;
|
||||||
const sectionWidth = r.u16(fields.sectionWidth);
|
const programWidth = programs.width;
|
||||||
let programCount = BigInt(r.u16(fields.programCount));
|
const sectionWidth = sections.width;
|
||||||
let sectionCount = BigInt(r.u16(fields.sectionCount));
|
let programCount = BigInt(programs.count);
|
||||||
let namesIndex = r.u16(fields.namesIndex);
|
let sectionCount = BigInt(sections.count);
|
||||||
|
let namesIndex = sections.namesIndex;
|
||||||
if (
|
if (
|
||||||
sectionOffset === 0n &&
|
sectionOffset === 0n &&
|
||||||
(sectionCount !== 0n || namesIndex !== 0 || programCount === 0xffffn)
|
(sectionCount !== 0n || namesIndex !== 0 || programCount === 0xffffn)
|
||||||
@@ -168,7 +145,7 @@ export function* readElfTableDeclarations(
|
|||||||
if (failure !== undefined) return err(failure);
|
if (failure !== undefined) return err(failure);
|
||||||
const first = yield {
|
const first = yield {
|
||||||
offset: Number(sectionOffset),
|
offset: Number(sectionOffset),
|
||||||
size: fields.sectionHeaderSize,
|
size: header.elfClass === 64 ? 64 : 40,
|
||||||
};
|
};
|
||||||
const counts = extendedCounts(first, header, {
|
const counts = extendedCounts(first, header, {
|
||||||
programCount,
|
programCount,
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { expect, it } from "vitest";
|
||||||
|
import { readElfRawTableDeclarations } from "./elfRawTableDeclarations.js";
|
||||||
|
|
||||||
|
const formats = [
|
||||||
|
{ elfClass: 32, byteOrder: "little" },
|
||||||
|
{ elfClass: 32, byteOrder: "big" },
|
||||||
|
{ elfClass: 64, byteOrder: "little" },
|
||||||
|
{ elfClass: 64, byteOrder: "big" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
it.each(formats)(
|
||||||
|
"preserves unresolved raw ELF$elfClass $byteOrder declarations",
|
||||||
|
(format) => {
|
||||||
|
const wide = format.elfClass === 64;
|
||||||
|
const little = format.byteOrder === "little";
|
||||||
|
const length = wide ? 64 : 52;
|
||||||
|
const backing = Buffer.alloc(length + 17, 0xa5);
|
||||||
|
const bytes = backing.subarray(7, 7 + length);
|
||||||
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||||
|
const programOffset = wide ? 0xfedcba9876543210n : 0xfedcba98n;
|
||||||
|
const sectionOffset = wide ? 0xffffffffffffffffn : 0xffffffffn;
|
||||||
|
if (wide) {
|
||||||
|
view.setBigUint64(32, programOffset, little);
|
||||||
|
view.setBigUint64(40, sectionOffset, little);
|
||||||
|
} else {
|
||||||
|
view.setUint32(28, Number(programOffset), little);
|
||||||
|
view.setUint32(32, Number(sectionOffset), little);
|
||||||
|
}
|
||||||
|
[1, 0xffff, 2, 0, 0xffff].forEach((value, index) =>
|
||||||
|
view.setUint16((wide ? 54 : 42) + index * 2, value, little),
|
||||||
|
);
|
||||||
|
const before = Buffer.from(backing);
|
||||||
|
const parsed = readElfRawTableDeclarations(backing.subarray(7), format);
|
||||||
|
expect(backing.equals(before)).toBe(true);
|
||||||
|
backing.fill(0);
|
||||||
|
expect(parsed).toEqual({
|
||||||
|
ok: true,
|
||||||
|
value: {
|
||||||
|
programs: { offset: programOffset, width: 1, count: 0xffff },
|
||||||
|
sections: {
|
||||||
|
offset: sectionOffset,
|
||||||
|
width: 2,
|
||||||
|
count: 0,
|
||||||
|
namesIndex: 0xffff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Format identity and whether these unresolved declarations are admissible belong to callers.
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(formats)(
|
||||||
|
"refuses truncated ELF$elfClass $byteOrder declaration records",
|
||||||
|
(format) => {
|
||||||
|
const length = format.elfClass === 64 ? 64 : 52;
|
||||||
|
const backing = Buffer.alloc(length + 7);
|
||||||
|
for (let observed = 0; observed < length; observed++)
|
||||||
|
expect(
|
||||||
|
readElfRawTableDeclarations(backing.subarray(7, 7 + observed), format),
|
||||||
|
).toEqual({
|
||||||
|
ok: false,
|
||||||
|
error: {
|
||||||
|
kind: "malformed",
|
||||||
|
message: "ELF table declarations are truncated",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { err, ok } from "./result.js";
|
||||||
|
|
||||||
|
/** Fixed-header table words, before extended numbering or file-range admission. */
|
||||||
|
export const readElfRawTableDeclarations = (
|
||||||
|
bytes: Uint8Array,
|
||||||
|
format: { readonly elfClass: 32 | 64; readonly byteOrder: "little" | "big" },
|
||||||
|
) => {
|
||||||
|
const wide = format.elfClass === 64;
|
||||||
|
if (bytes.byteLength < (wide ? 64 : 52))
|
||||||
|
return err({
|
||||||
|
kind: "malformed" as const,
|
||||||
|
message: "ELF table declarations are truncated",
|
||||||
|
});
|
||||||
|
const little = format.byteOrder === "little";
|
||||||
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||||
|
const word = (offset: number): bigint =>
|
||||||
|
wide
|
||||||
|
? view.getBigUint64(offset, little)
|
||||||
|
: BigInt(view.getUint32(offset, little));
|
||||||
|
return ok({
|
||||||
|
programs: {
|
||||||
|
offset: word(wide ? 32 : 28),
|
||||||
|
width: view.getUint16(wide ? 54 : 42, little),
|
||||||
|
count: view.getUint16(wide ? 56 : 44, little),
|
||||||
|
},
|
||||||
|
sections: {
|
||||||
|
offset: word(wide ? 40 : 32),
|
||||||
|
width: view.getUint16(wide ? 58 : 46, little),
|
||||||
|
count: view.getUint16(wide ? 60 : 48, little),
|
||||||
|
namesIndex: view.getUint16(wide ? 62 : 50, little),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -502,3 +502,22 @@ describe("readSnapshotHeader", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps section admission ahead of program admission for unrepresentable raw offsets", async () => {
|
||||||
|
const bytes = buildStrippedElf64Image([]);
|
||||||
|
bytes.writeBigUInt64LE(0xffffffffffffffffn, 0x28);
|
||||||
|
bytes.writeBigUInt64LE(0xffffffffffffffffn, 0x20);
|
||||||
|
expect(await readElfSymbols(bytes, ["_kDartVmSnapshotData"])).toEqual({
|
||||||
|
symbols: [],
|
||||||
|
failures: [
|
||||||
|
{ reason: "ELF section header table is missing or out of bounds" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
bytes.writeBigUInt64LE(0n, 0x28);
|
||||||
|
expect(await readElfSymbols(bytes, ["_kDartVmSnapshotData"])).toEqual({
|
||||||
|
symbols: [],
|
||||||
|
failures: [
|
||||||
|
{ reason: "ELF program header table is missing or out of bounds" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
+18
-15
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "../domain/elfRawTableDeclarations.js";
|
||||||
import { Buffer } from "node:buffer";
|
import { Buffer } from "node:buffer";
|
||||||
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
||||||
import { createElfLoadMap } from "./ElfLoadMap.js";
|
import { createElfLoadMap } from "./ElfLoadMap.js";
|
||||||
@@ -84,7 +85,6 @@ export const readElfHeader = (
|
|||||||
bytes: Buffer,
|
bytes: Buffer,
|
||||||
totalBytes: number = bytes.length,
|
totalBytes: number = bytes.length,
|
||||||
) => {
|
) => {
|
||||||
const reader = new BoundedReader(bytes);
|
|
||||||
const fixedHeader = readElfFixedHeader(bytes, totalBytes);
|
const fixedHeader = readElfFixedHeader(bytes, totalBytes);
|
||||||
if (!fixedHeader.ok) return { failure: { reason: fixedHeader.error } };
|
if (!fixedHeader.ok) return { failure: { reason: fixedHeader.error } };
|
||||||
const is64 = fixedHeader.value.elfClass === 64;
|
const is64 = fixedHeader.value.elfClass === 64;
|
||||||
@@ -96,21 +96,28 @@ export const readElfHeader = (
|
|||||||
const e_type = fixedHeader.value.type;
|
const e_type = fixedHeader.value.type;
|
||||||
if (e_type !== 1 && e_type !== 2 && e_type !== ET_DYN)
|
if (e_type !== 1 && e_type !== 2 && e_type !== ET_DYN)
|
||||||
return { failure: { reason: "unsupported ELF type" } };
|
return { failure: { reason: "unsupported ELF type" } };
|
||||||
const e_shoff = is64 ? reader.u64(0x28) : reader.u32(0x20);
|
const raw = readElfRawTableDeclarations(bytes, fixedHeader.value);
|
||||||
const e_shentsize = reader.u16(is64 ? 0x3a : 0x2e);
|
if (!raw.ok) return { failure: { reason: raw.error.message } };
|
||||||
const e_shnum = reader.u16(is64 ? 0x3c : 0x30);
|
const { programs, sections } = raw.value;
|
||||||
const e_phoff = is64 ? reader.u64(0x20) : reader.u32(0x1c);
|
const e_shoff =
|
||||||
const e_phentsize = reader.u16(is64 ? 0x36 : 0x2a);
|
sections.offset <= BigInt(Number.MAX_SAFE_INTEGER)
|
||||||
const e_phnum = reader.u16(is64 ? 0x38 : 0x2c);
|
? Number(sections.offset)
|
||||||
|
: null;
|
||||||
|
const e_shentsize = sections.width;
|
||||||
|
const e_shnum = sections.count;
|
||||||
|
const e_phoff =
|
||||||
|
programs.offset <= BigInt(Number.MAX_SAFE_INTEGER)
|
||||||
|
? Number(programs.offset)
|
||||||
|
: null;
|
||||||
|
const e_phentsize = programs.width;
|
||||||
|
const e_phnum = programs.count;
|
||||||
const tableFits = (
|
const tableFits = (
|
||||||
offset: number | null,
|
offset: number | null,
|
||||||
width: number | null,
|
width: number,
|
||||||
count: number | null,
|
count: number,
|
||||||
minimumWidth: number,
|
minimumWidth: number,
|
||||||
): boolean =>
|
): boolean =>
|
||||||
offset !== null &&
|
offset !== null &&
|
||||||
width !== null &&
|
|
||||||
count !== null &&
|
|
||||||
offset >= headerBytes &&
|
offset >= headerBytes &&
|
||||||
offset <= totalBytes &&
|
offset <= totalBytes &&
|
||||||
width >= minimumWidth &&
|
width >= minimumWidth &&
|
||||||
@@ -125,8 +132,6 @@ export const readElfHeader = (
|
|||||||
e_type === ET_DYN && e_shoff === 0 && e_shnum === 0;
|
e_type === ET_DYN && e_shoff === 0 && e_shnum === 0;
|
||||||
if (
|
if (
|
||||||
e_shoff === null ||
|
e_shoff === null ||
|
||||||
e_shentsize === null ||
|
|
||||||
e_shnum === null ||
|
|
||||||
(!sectionTableAbsent &&
|
(!sectionTableAbsent &&
|
||||||
!tableFits(e_shoff, e_shentsize, e_shnum, MIN_SHENT))
|
!tableFits(e_shoff, e_shentsize, e_shnum, MIN_SHENT))
|
||||||
)
|
)
|
||||||
@@ -138,8 +143,6 @@ export const readElfHeader = (
|
|||||||
const MIN_PHENT = is64 ? 56 : 32;
|
const MIN_PHENT = is64 ? 56 : 32;
|
||||||
if (
|
if (
|
||||||
e_phoff === null ||
|
e_phoff === null ||
|
||||||
e_phentsize === null ||
|
|
||||||
e_phnum === null ||
|
|
||||||
e_phnum === 0xffff ||
|
e_phnum === 0xffff ||
|
||||||
!tableFits(e_phoff, e_phentsize, e_phnum, MIN_PHENT)
|
!tableFits(e_phoff, e_phentsize, e_phnum, MIN_PHENT)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -617,3 +617,11 @@ it("fails when the entry budget is exhausted", () => {
|
|||||||
),
|
),
|
||||||
).toThrow(ArtifactReaderFailure);
|
).toThrow(ArtifactReaderFailure);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps unsafe table offsets ahead of unsupported extended numbering", () => {
|
||||||
|
const bytes = elfExtendedSectionCountFixture();
|
||||||
|
bytes.writeBigUInt64LE(0xffffffffffffffffn, 32);
|
||||||
|
expect(() => parse(bytes)).toThrow(
|
||||||
|
"ELF offset or size exceeds a safe integer.",
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "../../domain/elfRawTableDeclarations.js";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||||
import { readElfFixedHeader } from "../../domain/elfFixedHeader.js";
|
import { readElfFixedHeader } from "../../domain/elfFixedHeader.js";
|
||||||
@@ -98,13 +99,6 @@ class ElfReader {
|
|||||||
readonly bits: 32 | 64,
|
readonly bits: 32 | 64,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
u16(offset: number): number {
|
|
||||||
this.require(offset, 2);
|
|
||||||
return this.little
|
|
||||||
? this.bytes.readUInt16LE(offset)
|
|
||||||
: this.bytes.readUInt16BE(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32(offset: number): number {
|
u32(offset: number): number {
|
||||||
this.require(offset, 4);
|
this.require(offset, 4);
|
||||||
return this.little
|
return this.little
|
||||||
@@ -328,13 +322,21 @@ export const parseElfNotes = (
|
|||||||
);
|
);
|
||||||
const wide = header.elfClass === 64;
|
const wide = header.elfClass === 64;
|
||||||
const type = header.type;
|
const type = header.type;
|
||||||
const phoff = reader.word(wide ? 32 : 28);
|
const raw = readElfRawTableDeclarations(bytes, header);
|
||||||
const shoff = reader.word(wide ? 40 : 32);
|
if (!raw.ok) return fail(raw.error.message);
|
||||||
const phentsize = reader.u16(wide ? 54 : 42);
|
const { programs, sections } = raw.value;
|
||||||
const phnum = reader.u16(wide ? 56 : 44);
|
if (
|
||||||
const shentsize = reader.u16(wide ? 58 : 46);
|
programs.offset > BigInt(Number.MAX_SAFE_INTEGER) ||
|
||||||
const shnum = reader.u16(wide ? 60 : 48);
|
sections.offset > BigInt(Number.MAX_SAFE_INTEGER)
|
||||||
const shstrndx = reader.u16(wide ? 62 : 50);
|
)
|
||||||
|
return fail("ELF offset or size exceeds a safe integer.");
|
||||||
|
const phoff = Number(programs.offset);
|
||||||
|
const shoff = Number(sections.offset);
|
||||||
|
const phentsize = programs.width;
|
||||||
|
const phnum = programs.count;
|
||||||
|
const shentsize = sections.width;
|
||||||
|
const shnum = sections.count;
|
||||||
|
const shstrndx = sections.namesIndex;
|
||||||
if (
|
if (
|
||||||
phnum === 0xffff ||
|
phnum === 0xffff ||
|
||||||
shnum === 0xffff ||
|
shnum === 0xffff ||
|
||||||
|
|||||||
@@ -50,3 +50,14 @@ it.each([3, 0x46494c45])(
|
|||||||
expect(recordedCrashSchema.safeParse(report).success).toBe(false);
|
expect(recordedCrashSchema.safeParse(report).success).toBe(false);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it("keeps snapshot rebinding independent of unused ELF section and header-size admission", () => {
|
||||||
|
const report = recordedCrashSchema.parse(recordedCrashFixture());
|
||||||
|
const bytes = recordedCrashFixtureBytes(report);
|
||||||
|
bytes.writeUInt16LE(0, 52);
|
||||||
|
bytes.writeBigUInt64LE(0xffffffffffffffffn, 40);
|
||||||
|
bytes.writeUInt16LE(0xffff, 60);
|
||||||
|
expect(() =>
|
||||||
|
validateRecordedCrashStructure(report, bytes, report.decoder_diagnostics),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { readElfRawTableDeclarations } from "../../domain/elfRawTableDeclarations.js";
|
||||||
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
|
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
|
||||||
import {
|
import {
|
||||||
RECORDED_LINUX_CORE_PROFILES,
|
RECORDED_LINUX_CORE_PROFILES,
|
||||||
@@ -103,12 +104,18 @@ export function validateRecordedCrashStructure(
|
|||||||
header.readUInt16LE(18) !== expectedMachine
|
header.readUInt16LE(18) !== expectedMachine
|
||||||
)
|
)
|
||||||
fail();
|
fail();
|
||||||
const table = header.readBigUInt64LE(32);
|
const rawTables = readElfRawTableDeclarations(header, {
|
||||||
const entryBytes = header.readUInt16LE(54);
|
elfClass: 64,
|
||||||
let count = header.readUInt16LE(56);
|
byteOrder: "little",
|
||||||
|
});
|
||||||
|
if (!rawTables.ok) return fail();
|
||||||
|
const { programs, sections } = rawTables.value;
|
||||||
|
const table = programs.offset;
|
||||||
|
const entryBytes = programs.width;
|
||||||
|
let count = programs.count;
|
||||||
if (count === 0xffff) {
|
if (count === 0xffff) {
|
||||||
if (header.readUInt16LE(58) < 64) return fail();
|
if (sections.width < 64) return fail();
|
||||||
count = bytesAt(header.readBigUInt64LE(40), 64).readUInt32LE(44);
|
count = bytesAt(sections.offset, 64).readUInt32LE(44);
|
||||||
}
|
}
|
||||||
if (count !== report.segments.length || (count > 0 && entryBytes < 56))
|
if (count !== report.segments.length || (count > 0 && entryBytes < 56))
|
||||||
fail();
|
fail();
|
||||||
|
|||||||
Reference in new issue
Block a user