fix(ghidra): retain neutral recovery for observed SIGKILL

This commit is contained in:
morluto
2026-10-24 04:16:07 +08:00
parent 80d4a74a06
commit ecfac46c8d
3 changed files with 49 additions and 0 deletions
+6
View File
@@ -24,6 +24,12 @@ undefined. Installation `application.properties` has a fixed 1 MiB read allowanc
with no runtime override. The failure retains that allowance and directs recovery
at the selected installation metadata.
Retained process diagnostics can report termination by `SIGKILL`; the signal alone
cannot establish an OOM kill. REA gives neutral recovery guidance and preserves the
signal. Confirm resource causes from process output or host/container records;
operation-scoped JVM `OutOfMemoryError` observations retain their separate resource
classification. Automatic kernel or cgroup OOM attribution is not implemented.
Provider selection and profile identity follow
[MCP contracts](mcp-contracts.md#identity-and-discovery). One deep
provider stays bound to the target; runtime failure never selects another
@@ -3690,6 +3690,37 @@ const issueOutput = [
].join("\n");
describe("Ghidra OOME error projection", () => {
it("preserves observed SIGKILL without guessing its cause or installation recovery", async () => {
const projected = await projectFailure("process", {
exit_code: null,
exit_signal: "SIGKILL",
failure_output: failureOutput(),
});
expect(projected.code).toBe("execution_failure");
expect(projected.message).toContain("SIGKILL");
expect(projected.message).toContain("does not establish its cause");
expect(projected.remediation.action).toContain(
"host or container termination records",
);
expect(projected.remediation.action).not.toContain("rea doctor");
expect(projected.remediation.action).not.toContain(
"GHIDRA_HEADLESS_MAXMEM",
);
});
it("keeps operation-scoped JVM memory evidence stronger than an exit signal", async () => {
const projected = await projectFailure("process", {
exit_signal: "SIGKILL",
failure_output: failureOutput(
"",
"java.lang.OutOfMemoryError: Java heap space",
),
});
expect(projected.code).toBe("resource_constraint");
expect(projected.message).toContain("Java heap space");
expect(projected.remediation.action).toContain("GHIDRA_HEADLESS_MAXMEM");
});
it("recognizes the headless analyzer's real detail-before-class output", async () => {
const projected = await projectFailure("process", {
stdout: output(issueOutput),
+12
View File
@@ -1008,6 +1008,18 @@ const projectPrimarySessionError = (
}),
});
}
if (
(failure.kind === "start" ||
failure.kind === "process" ||
failure.kind === "protocol") &&
failure.diagnostics.exit_signal === "SIGKILL"
)
return new ProviderAdapterError("ghidra", operation, {
...context,
userMessage: `Ghidra process diagnostics report termination by SIGKILL. The signal alone does not establish its cause or an out-of-memory failure.${uncertainty === undefined ? "" : ` ${uncertainty}`}`,
remediationAction:
"Review the retained process output and host or container termination records before choosing a resource-specific remedy. Export useful Evidence, close the binary and resolve any cleanup failure, then call open_binary for the same target to start a new Ghidra session. Session-only annotations are not retained.",
});
const exited = exitedProviderMessage(operation, failure, health);
if (exited !== undefined)
return new ProviderAdapterError("ghidra", operation, {