Files
rea/scripts/lib/elf-section-name-fixtures.mjs
Ysopwnable 4fc6565b0f feat(native): inspect offline ELF layout through pwntools (#1037)
* feat(native): inspect offline ELF layout through pwntools

* fix(build): prepare offline ELF verification on clean runners

* fix(native): preserve snapshot acquisition cancellation

* fix(native): handle sectionless ELF and retain decoder diagnostics

* test(native): verify malformed symbol table entry sizes

* fix(native): respect inherited limits and unused segment fields

* fix(native): preserve packed relocations and decoder launch constraints

* fix(native): validate symbol spans and preserve resource failures

* fix(native): preserve diagnostic completeness and memory failures

* fix(native): guard bridge imports against allocation failures

* fix(native): validate relocation symbol and section references

* fix(native): retain diagnostics through compounded cleanup failures

* fix(native): preserve unresolved relocation section references

* fix(native): bound linkage evidence and preserve CPU signal diagnostics

* fix(native): preserve file-size resource failure evidence

* fix(native): retain opaque linkage names and late cancellation output

* test(native): make CPU-limit fixture robust across runners

* fix(native): verify resource failure branches before interpreting exits

* fix(native): validate declared ELF string references

* fix(native): validate section-name table before decoding

* fix(native): enforce canonical ELF name-table escapes
2026-10-08 14:22:08 +08:00

89 lines
3.3 KiB
JavaScript

import assert from "node:assert/strict";
/** Source-owned ELF64 mutations for declared, absent and escaped name-table references. */
export const sectionNameFixtures = (original, report) => {
// ELF gABI: https://gabi.xinuos.com/elf/02-eheader.html#e-shstrndx
// SHN_XINDEX must carry an actual index >= SHN_LORESERVE, not a small index.
const sourceTable = report.sections[original.readUInt16LE(62)];
assert.equal(sourceTable.type, "SHT_STRTAB");
const sourceHeader = Number(BigInt(sourceTable.header_location.offset));
const fixtures = [];
for (const problem of [
"out-of-range",
"wrong-type",
"undeclared-header",
"extended-undeclared-header",
"extended-without-sections",
"extended-absent",
"extended-small",
]) {
let bytes = Buffer.from(original);
let selected = report.sections.length + 100;
if (problem === "wrong-type")
selected = report.sections.find(
(section) => section.type === "SHT_PROGBITS",
).index;
else if (problem.includes("undeclared-header")) {
const end =
Number(bytes.readBigUInt64LE(40)) +
report.sections.length * bytes.readUInt16LE(58);
const extended = Buffer.alloc(Math.max(bytes.length, end + 64));
bytes.copy(extended);
bytes.copy(extended, end, sourceHeader, sourceHeader + 64);
bytes = extended;
selected = report.sections.length;
}
if (problem === "extended-without-sections") {
bytes.writeBigUInt64LE(0n, 40);
bytes.writeUInt16LE(0, 60);
bytes.writeUInt16LE(0xffff, 62);
} else if (problem.startsWith("extended-")) {
bytes.writeUInt16LE(0xffff, 62);
if (problem === "extended-absent") selected = 0;
if (problem === "extended-small") selected = sourceTable.index;
bytes.writeUInt32LE(selected, Number(bytes.readBigUInt64LE(40)) + 40);
} else bytes.writeUInt16LE(selected, 62);
fixtures.push({ name: problem, bytes, expectation: "invalid_input" });
}
const absent = Buffer.from(original);
absent.writeUInt16LE(0, 62);
fixtures.push({ name: "absent", bytes: absent, expectation: "absent" });
// A genuine escape requires a large table. Relocate the original table, keep
// its referenced indices and move only the name table to index SHN_LORESERVE.
const selected = 0xff00,
count = selected + 1;
const start = Math.ceil(original.length / 8) * 8;
const large = Buffer.alloc(start + count * 64);
original.copy(large);
const oldStart = Number(original.readBigUInt64LE(40));
original.copy(large, start, oldStart, oldStart + report.sections.length * 64);
large.fill(
0,
start + sourceTable.index * 64,
start + (sourceTable.index + 1) * 64,
);
original.copy(large, start + selected * 64, sourceHeader, sourceHeader + 64);
large.writeBigUInt64LE(BigInt(start), 40);
large.writeUInt16LE(0, 60);
large.writeUInt16LE(0xffff, 62);
large.writeBigUInt64LE(BigInt(count), start + 32);
large.writeUInt32LE(selected, start + 40);
fixtures.push({
name: "extended-large",
bytes: large,
expectation: "resolved",
tableIndex: selected,
sectionCount: count,
});
const reserved = Buffer.from(large);
reserved.writeUInt16LE(selected, 62);
reserved.writeUInt32LE(0, start + 40);
fixtures.push({
name: "direct-reserved",
bytes: reserved,
expectation: "invalid_input",
});
return fixtures;
};