mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
fix(ida): admit architecture families by allowlist (#7485)
IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so any family nobody excluded was reported supported, and the label ternary printed "Hexagon" for any other metadata family. Admit exactly the families IDA admits today (x86, x86_64, arm, arm64) and name every other family from its own architecture id. Families that Ghidra admits keep the pointer to a Ghidra profile. Refusal text now uses architecture ids, for example "no verified mips lane" instead of "no verified MIPS lane".
This commit is contained in:
1 parent
b33f4a5a1b
commit
1926fc2dac
3 files changed
+62
-27
No files matched your search
@@ -220,6 +220,12 @@ For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <cl
|
||||
|
||||
## Coverage and evidence
|
||||
|
||||
REA admits x86, x86_64, arm and arm64 executables through IDA. Every other CPU
|
||||
family, including families that REA recognizes later, is refused until REA
|
||||
admits it explicitly. Raw-binary and GameCube DOL targets are refused; open
|
||||
them with Ghidra. Admission is not verification: the real workflows under
|
||||
[Verification](#verification) cover x86_64 and arm64 only.
|
||||
|
||||
All profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. In the modern headless profile, targets that are not proven x86-only use processor-neutral `callgraph` edges instead of the x86 instruction checks in upstream `callees` (mrexodia/ida-pro-mcp#549). REA requests depth zero: the pinned upstream emits the root's outgoing edges without scanning child functions, then REA batches function-name lookup for those addresses. The graph covers recognized function destinations only; imports and other non-function targets remain unknown. A server without `callgraph` returns an empty relationship list with an explicit coverage limitation. Graph and path workflows retain those limitations alongside observed edges. A failed or inconsistent graph reply remains an error. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
|
||||
|
||||
All profiles expose `list_documents` as the single target admitted for the session. Attached mode verifies its digest against IDA metadata before returning it; headless mode binds the digest-checked private input. `inspect_address_context`, `trace_feature`, and `binary_overview` can therefore resolve that identity without upstream document-listing tools. Segment enumeration is optional: when the selected profile cannot provide it, `binary_overview` still returns document and procedure/string counts and omits `segments` and `segment_count`; omission means unobserved, not empty. The modern headless profile also supports `resolve_containing_procedure`. An address resolves through upstream `lookup_funcs` to the canonical entry of the function that contains it; the body stays unavailable like other IDA identities. When no function contains it, REA reads the database-scoped minimal `survey_binary` and reports `not_in_procedure` for an address inside one of its segments (ends are exclusive) and `outside_segments` otherwise. A missing, truncated or inconsistent survey fails the read instead of guessing a reason. `trace_feature` marks a trace truncated, with a residual unknown for that address, when upstream caps the references it returned. Attached profiles do not declare address containment resolution.
|
||||
|
||||
@@ -7,6 +7,10 @@ import { join } from "node:path";
|
||||
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
||||
|
||||
import { parseConfig } from "../config/parseConfig.js";
|
||||
import {
|
||||
BINARY_ARCHITECTURES,
|
||||
type BinaryTarget,
|
||||
} from "../domain/binaryTargetTypes.js";
|
||||
import {
|
||||
RecordingIdaMcp,
|
||||
createIdaTarget,
|
||||
@@ -208,9 +212,36 @@ describe("IDA provider target support", () => {
|
||||
).toMatchObject({
|
||||
status: "unsupported",
|
||||
code: "architecture_unsupported",
|
||||
reason: expect.stringContaining("Hexagon"),
|
||||
reason: expect.stringMatching(/hexagon.*Ghidra/su),
|
||||
});
|
||||
});
|
||||
it("admits only the families IDA admits today, so new families stay refused", () => {
|
||||
const config = parseConfig({});
|
||||
if (!config.ok) throw new Error("expected valid configuration");
|
||||
const provider = new IdaProvider(config.value, {});
|
||||
const admitted: readonly string[] = ["x86", "x86_64", "arm", "arm64"];
|
||||
for (const architecture of BINARY_ARCHITECTURES) {
|
||||
// Admission reads only kind, format and family, so one ELF shape
|
||||
// covers every recognized family, including any added later.
|
||||
const support = provider.inspectTargetSupport({
|
||||
path: "/tmp/fixture.elf",
|
||||
sha256: "a".repeat(64),
|
||||
kind: "executable",
|
||||
format: "elf",
|
||||
architecture,
|
||||
availableArchitectures: [architecture],
|
||||
} as BinaryTarget);
|
||||
expect(support, architecture).toMatchObject(
|
||||
admitted.includes(architecture)
|
||||
? { status: "supported" }
|
||||
: {
|
||||
status: "unsupported",
|
||||
code: "architecture_unsupported",
|
||||
reason: expect.stringContaining(architecture),
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
it("declines thin arm64_32 Mach-O targets without a verified lane", () => {
|
||||
const config = parseConfig({});
|
||||
if (!config.ok) throw new Error("expected valid configuration");
|
||||
|
||||
+24
-26
@@ -8,6 +8,7 @@ import {
|
||||
import type { AnalysisProfileCommitment } from "../domain/analysisProfile.js";
|
||||
import {
|
||||
isElfMetadataArchitecture,
|
||||
type BinaryArchitecture,
|
||||
type BinaryTarget,
|
||||
} from "../domain/binaryTargetTypes.js";
|
||||
import { err, ok } from "../domain/result.js";
|
||||
@@ -53,6 +54,17 @@ import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
||||
import type { ProviderInspectionScope } from "../application/ProviderInspectionScope.js";
|
||||
import type { ProviderRejectionCode } from "../domain/providerSelection.js";
|
||||
|
||||
/**
|
||||
* Families IDA admits today. Every other family is refused, including families
|
||||
* that REA recognizes after this list was written.
|
||||
*/
|
||||
const IDA_ARCHITECTURES = new Set<BinaryArchitecture>([
|
||||
"x86",
|
||||
"x86_64",
|
||||
"arm",
|
||||
"arm64",
|
||||
]);
|
||||
|
||||
/** Bring-your-own IDA MCP candidate; discovery performs no provider startup. */
|
||||
export class IdaProvider implements AnalysisProviderCandidate {
|
||||
readonly #ownedResources = new ArtifactResourceScope();
|
||||
@@ -300,32 +312,6 @@ export class IdaProvider implements AnalysisProviderCandidate {
|
||||
"IDA does not admit caller-selected GameCube DOL images. Open the target with Ghidra and select format gamecube-dol.",
|
||||
diagnostics,
|
||||
};
|
||||
if (
|
||||
target.kind === "executable" &&
|
||||
(isElfMetadataArchitecture(target.architecture) ||
|
||||
target.architecture === "hexagon")
|
||||
)
|
||||
return {
|
||||
status: "unsupported",
|
||||
code: "architecture_unsupported",
|
||||
reason: `REA's IDA adapter has no verified ${
|
||||
target.architecture === "mips"
|
||||
? "MIPS"
|
||||
: target.architecture === "riscv"
|
||||
? "RISC-V"
|
||||
: target.architecture === "powerpc"
|
||||
? "PowerPC"
|
||||
: "Hexagon"
|
||||
} lane. Select a supported Ghidra profile instead.`,
|
||||
diagnostics,
|
||||
};
|
||||
if (target.kind === "executable" && target.architecture === "arm64_32")
|
||||
return {
|
||||
status: "unsupported",
|
||||
code: "architecture_unsupported",
|
||||
reason: "REA's IDA adapter has no verified arm64_32 lane.",
|
||||
diagnostics,
|
||||
};
|
||||
if (target.kind !== "executable")
|
||||
return {
|
||||
status: "unsupported",
|
||||
@@ -342,6 +328,18 @@ export class IdaProvider implements AnalysisProviderCandidate {
|
||||
"IDA does not admit headerless raw binaries. Open the target with Ghidra and supply architecture and entry_point.",
|
||||
diagnostics,
|
||||
};
|
||||
if (!IDA_ARCHITECTURES.has(target.architecture))
|
||||
return {
|
||||
status: "unsupported",
|
||||
code: "architecture_unsupported",
|
||||
// Header-declared families that Ghidra admits keep the pointer there.
|
||||
reason:
|
||||
isElfMetadataArchitecture(target.architecture) ||
|
||||
target.architecture === "hexagon"
|
||||
? `REA's IDA adapter has no verified ${target.architecture} lane. Select a supported Ghidra profile instead.`
|
||||
: `REA's IDA adapter has no verified ${target.architecture} lane.`,
|
||||
diagnostics,
|
||||
};
|
||||
return target.format === "mach-o-universal"
|
||||
? {
|
||||
status: "unknown",
|
||||
|
||||
Reference in new issue
Block a user