mirror of
https://github.com/morluto/rea.git
synced 2026-10-11 21:39:55 +00:00
fix(wasm): accept unpadded WABT section rows (#1963)
* fix(wasm): accept unpadded WABT section rows WABT 1.0.42 right-aligns section names in a nine-column field. DataCount fills that field, so its objdump row has no leading whitespace. Accept that legal row. Header recognition, the required field shape, and section range checks still reject malformed output. Fixes #1956 * fix(ci): refresh MIPS expectations and Windows test paths Assert admitted soft-float ABI metadata and its committed storage policy through the public target resolver, retain FP64 rejection, and point the curated Windows lane at the moved filesystem reconstruction tests. Validation: reproduced all four prior failures; 97 focused tests and check:ci pass. Native Windows and real Ghidra execution were not performed locally. --------- Co-authored-by: morluto <[email protected]>
This commit is contained in:
co-authored by
morluto
parent
beb80a07ba
commit
9eee709f0c
@@ -493,7 +493,7 @@ jobs:
|
||||
tests/acceptance/webSourceLocationCli.test.ts
|
||||
tests/boundary/cli/referenceSourceImportStatus.test.ts
|
||||
tests/boundary/filesystem/asarInventoryFreshness.test.ts
|
||||
tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
|
||||
tests/boundary/filesystem/javascript-applications/javascriptArtifactReconstruction.test.ts
|
||||
tests/conformance/providers/ghidra/windowsCapabilities.test.ts
|
||||
src/ghidra/GhidraInstallation.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTransport.test.ts
|
||||
|
||||
+2
-1
@@ -1386,7 +1386,8 @@ generation. It installs nothing. Without configuration, the default invocation
|
||||
reports a named skip; `--require-tools` makes missing configuration fail.
|
||||
|
||||
The verifier creates and validates real modules with custom sections, multiple
|
||||
import/export kinds, escaped names and empty modules. It compares exact WAT,
|
||||
import/export kinds, escaped names, an empty module, and a bulk-memory
|
||||
DataCount section. It compares exact WAT,
|
||||
section output and selected/tool digests through the compiled CLI and real MCP
|
||||
SDK, validates advertised schemas, checks identical CLI/MCP Evidence IDs and
|
||||
session bundle readback, retains invalid/truncated module diagnostics, preserves
|
||||
|
||||
@@ -122,6 +122,15 @@ try {
|
||||
new URL("../tests/fixtures/wasm/custom-names.wat", import.meta.url),
|
||||
"utf8",
|
||||
),
|
||||
`(module
|
||||
(memory 1)
|
||||
(data "x")
|
||||
(func (export "init")
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 1
|
||||
memory.init 0
|
||||
data.drop 0))\n`,
|
||||
];
|
||||
for (const [index, source] of sources.entries()) {
|
||||
const fixtureRoot = join(root.path, `fixture-${index}`);
|
||||
@@ -220,6 +229,22 @@ try {
|
||||
'"unicode-é\\\t"',
|
||||
],
|
||||
);
|
||||
if (index === 4) {
|
||||
assert.match(
|
||||
headers,
|
||||
/^DataCount start=0x[\da-f]+ end=0x[\da-f]+ \(size=0x[\da-f]+\) count: 1$/m,
|
||||
);
|
||||
assert.deepEqual(
|
||||
report.sections.find(({ kind }) => kind === "DataCount"),
|
||||
{
|
||||
kind: "DataCount",
|
||||
start: 0x23,
|
||||
end: 0x24,
|
||||
bytes: 1,
|
||||
description: "count: 1",
|
||||
},
|
||||
);
|
||||
}
|
||||
const bundleReply = await client.callTool({
|
||||
name: "get_evidence_bundle",
|
||||
arguments: {},
|
||||
|
||||
@@ -33,6 +33,110 @@ it("retains real WABT section ranges, custom section identity and original heade
|
||||
["test"],
|
||||
),
|
||||
).toThrow(/Unrecognized/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
text.replace(
|
||||
"\nSection Details:\n",
|
||||
"\nDataCount start=0x00000040 end=0x0000004c (size=0x0000000c)\nSection Details:\n",
|
||||
),
|
||||
76,
|
||||
["test"],
|
||||
),
|
||||
).toThrow(/Unrecognized/u);
|
||||
});
|
||||
const dataCountModuleBytes = 0x3d;
|
||||
const unpaddedDataCountRow =
|
||||
"DataCount start=0x00000023 end=0x00000024 (size=0x00000001) count: 1";
|
||||
const dataCountDump = [
|
||||
"",
|
||||
"module.wasm:\tfile format wasm 0x1",
|
||||
"",
|
||||
"Sections:",
|
||||
"",
|
||||
" Type start=0x0000000a end=0x0000000e (size=0x00000004) count: 1",
|
||||
" Function start=0x00000010 end=0x00000012 (size=0x00000002) count: 1",
|
||||
" Memory start=0x00000014 end=0x00000017 (size=0x00000003) count: 1",
|
||||
" Export start=0x00000019 end=0x00000021 (size=0x00000008) count: 1",
|
||||
unpaddedDataCountRow,
|
||||
" Code start=0x00000026 end=0x00000037 (size=0x00000011) count: 1",
|
||||
" Data start=0x00000039 end=0x0000003d (size=0x00000004) count: 1",
|
||||
"",
|
||||
"Section Details:",
|
||||
"",
|
||||
].join("\n");
|
||||
it("accepts an unpadded DataCount row and still rejects malformed output and bad ranges", () => {
|
||||
const parsed = parseWabtOutput(dataCountDump, dataCountModuleBytes, []);
|
||||
expect(parsed.sections.map(({ kind }) => kind)).toEqual([
|
||||
"Type",
|
||||
"Function",
|
||||
"Memory",
|
||||
"Export",
|
||||
"DataCount",
|
||||
"Code",
|
||||
"Data",
|
||||
]);
|
||||
expect(parsed.sections.find(({ kind }) => kind === "DataCount")).toEqual({
|
||||
kind: "DataCount",
|
||||
start: 0x23,
|
||||
end: 0x24,
|
||||
bytes: 1,
|
||||
description: "count: 1",
|
||||
});
|
||||
expect(parsed.headers).toContain(`\n${unpaddedDataCountRow}\n`);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace(unpaddedDataCountRow, "DataCount count: 1"),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/Unrecognized/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace(unpaddedDataCountRow, "Sections:"),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/Unrecognized/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace(
|
||||
unpaddedDataCountRow,
|
||||
"DataCount start=0x00000023 end=0x00000024 (size=0x00000001)count: 1",
|
||||
),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/Unrecognized/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace("(size=0x00000001)", "(size=0x00000002)"),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/ranges/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace(
|
||||
"DataCount start=0x00000023 end=0x00000024 (size=0x00000001)",
|
||||
"DataCount start=0x00000020 end=0x00000021 (size=0x00000001)",
|
||||
),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/ranges/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(dataCountDump, dataCountModuleBytes - 1, []),
|
||||
).toThrow(/ranges/u);
|
||||
expect(() =>
|
||||
parseWabtOutput(
|
||||
dataCountDump.replace(
|
||||
"\nmodule.wasm:\tfile format wasm 0x1\n\nSections:\n\n",
|
||||
"",
|
||||
),
|
||||
dataCountModuleBytes,
|
||||
[],
|
||||
),
|
||||
).toThrow(/header/u);
|
||||
});
|
||||
it("extracts escaped WAT import/export forms while retaining multiline producer text", async () => {
|
||||
const text = await readFile(
|
||||
|
||||
@@ -13,7 +13,7 @@ export const parseWabtOutput = (
|
||||
const sections = [];
|
||||
while (!text.startsWith("\nSection Details:\n", offset)) {
|
||||
const match =
|
||||
/^[ \t]+(\w+) start=0x([\da-f]+) end=0x([\da-f]+) \(size=0x([\da-f]+)\) /u.exec(
|
||||
/^[ \t]*(\w+) start=0x([\da-f]+) end=0x([\da-f]+) \(size=0x([\da-f]+)\) /u.exec(
|
||||
text.slice(offset),
|
||||
);
|
||||
if (match === null) throw new Error("Unrecognized WABT section row.");
|
||||
|
||||
@@ -131,7 +131,7 @@ for (const little of [true, false]) {
|
||||
const fp64 = await inspect(bytes, little);
|
||||
assert.ok(fp64.ok);
|
||||
const changed = target({ ...metadata(little), abiFlags: fp64.value });
|
||||
assert.match(ghidraMipsUnsupportedReason(changed) ?? "", /register modes/u);
|
||||
assert.match(ghidraMipsUnsupportedReason(changed) ?? "", /FP64/u);
|
||||
assert.notDeepEqual(ghidraMipsProfileParameters(changed), before);
|
||||
});
|
||||
}
|
||||
@@ -271,14 +271,6 @@ const unsupported: [string, (bytes: Buffer) => void, RegExp][] = [
|
||||
},
|
||||
/MIPS32r2/u,
|
||||
],
|
||||
[
|
||||
"soft-float",
|
||||
(b) => {
|
||||
b[abiOffset + 5] = 0;
|
||||
b[abiOffset + 7] = 3;
|
||||
},
|
||||
/register modes/u,
|
||||
],
|
||||
[
|
||||
"unspecified FP ABI",
|
||||
(b) => {
|
||||
@@ -309,6 +301,19 @@ for (const [name, change, reason] of unsupported) {
|
||||
);
|
||||
});
|
||||
}
|
||||
it("resolves soft-float ABI metadata and commits its integer-register storage policy", async () => {
|
||||
const bytes = fixture();
|
||||
bytes[abiOffset + 5] = 0;
|
||||
bytes[abiOffset + 7] = 3;
|
||||
const resolved = await resolveFixture(bytes);
|
||||
assert.equal(resolved.mips.abiFlags?.cpr1Size, 0);
|
||||
assert.equal(resolved.mips.abiFlags?.fpAbi, 3);
|
||||
assert.equal(ghidraMipsUnsupportedReason(resolved), null);
|
||||
assert.equal(
|
||||
ghidraMipsProfileParameters(resolved).mips_soft_float_storage,
|
||||
"o32-soft-float-prototype-v1",
|
||||
);
|
||||
});
|
||||
it("preserves the caller's cancellation error during table inspection", async () => {
|
||||
const cancelled = new Error("caller cancellation sentinel");
|
||||
let checks = 0;
|
||||
|
||||
Reference in New Issue
Block a user