mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 17:28:08 +00:00
fix(ghidra)!: distinguish unlisted instruction addresses (#7823)
* fix(ghidra)!: distinguish unlisted instruction addresses BREAKING CHANGE: Ghidra now returns not-in-listing for in-memory addresses without listed instructions or defined data. Consumers matching the former undecodable value must handle the new status. * fix(ghidra): invalidate snapshots with old instruction status * test(ghidra): update raw profile commitment goldens
This commit is contained in:
1 parent
b0880d660f
commit
bd687bbc40
8 files changed
+83
-16
No files matched your search
@@ -13,8 +13,10 @@ import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { Ajv2020 } from "ajv/dist/2020.js";
|
||||
import { parseEvidence } from "../dist/domain/evidence.js";
|
||||
import { functionDossierSchema } from "../dist/domain/native/functionDossier.js";
|
||||
import { nativeInstructionSchema } from "../dist/domain/native/nativeInstruction.js";
|
||||
import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-inspection.mjs";
|
||||
import { buildDosComFixture } from "../tests/conformance/ghidra/dos-com-fixture.mjs";
|
||||
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
|
||||
@@ -158,6 +160,54 @@ try {
|
||||
instruction.bytes.replaceAll(" ", "").toLowerCase(),
|
||||
fixture.entry_hex,
|
||||
);
|
||||
// This NOP follows the entry's terminating INT 20h and is not reached by
|
||||
// either function. It remains valid instruction bytes outside the listing.
|
||||
const unlistedAddress = "0x1013f";
|
||||
const unlistedBytes = await call("read_bytes", {
|
||||
address: unlistedAddress,
|
||||
length: 1,
|
||||
});
|
||||
assert.equal(unlistedBytes.bytes_hex, "90");
|
||||
const unlistedResponse = await client.callTool({
|
||||
name: "inspect_native_instruction",
|
||||
arguments: { address: unlistedAddress },
|
||||
});
|
||||
const unlistedEvidence = parseEvidence(
|
||||
requireMcpOutput(unlistedResponse, "inspect_native_instruction"),
|
||||
);
|
||||
const unlisted = unlistedEvidence.normalized_result;
|
||||
assert.equal(unlisted.status, "not-in-listing");
|
||||
assert.equal(unlisted.bytes, null);
|
||||
assert.ok(
|
||||
unlisted.limitations.some((item) =>
|
||||
item.includes("did not attempt a decode"),
|
||||
),
|
||||
);
|
||||
const advertised = (await client.listTools()).tools.find(
|
||||
(tool) => tool.name === "inspect_native_instruction",
|
||||
);
|
||||
assert.ok(advertised?.outputSchema);
|
||||
const ajv = new Ajv2020({ strict: false, validateFormats: false });
|
||||
assert.equal(ajv.validateSchema(advertised.outputSchema), true);
|
||||
assert.equal(
|
||||
ajv.validate(advertised.outputSchema, unlistedResponse.structuredContent),
|
||||
true,
|
||||
JSON.stringify(ajv.errors),
|
||||
);
|
||||
assert.ok(
|
||||
nativeInstructionSchema.safeParse({ ...unlisted, status: "undecodable" })
|
||||
.success,
|
||||
);
|
||||
assert.equal(
|
||||
(await call("resolve_native_call_targets", { address: unlistedAddress }))
|
||||
.status,
|
||||
"unavailable",
|
||||
);
|
||||
assert.deepEqual(
|
||||
(await cli("inspect-native-instruction", unlistedAddress))
|
||||
.normalized_result,
|
||||
unlisted,
|
||||
);
|
||||
const cliImage = await cli("inspect-native-load-image");
|
||||
const stable = (x) => ({
|
||||
...x,
|
||||
|
||||
Reference in new issue
Block a user