Files
rea/src/native/elf/ElfNotesProvider.ts
T
Matt Van Horn 2edf3b23c1 feat(elf): inspect PT_NOTE and SHT_NOTE tables (#4837)
Inspect selected ELF32/ELF64 PT_NOTE and SHT_NOTE declarations without executing
the artifact, retaining independent table identities, source ranges, descriptor
digests, GNU build-id/ABI fields and explicit coverage limitations.

Integrate mvanhorn's implementation and hardening with current main while
preserving byte-entropy registration and the Windows curated regression suite.
The maintainer follow-up uses ordinary merges/pushes, not history rewriting.

Executed validation: 96 focused/shared/docs tests, complete static/docs checks,
eleven real CLI/MCP pairs including GCC ELF32 and Clang big-endian PowerPC64,
and two actual Codex task traces checked against their Evidence. Full costs,
grader correction and verification limits are recorded in PR #4837.

Refs #6262.
2026-10-22 15:02:23 +08:00

208 lines
7.2 KiB
TypeScript

import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
import {
ArtifactFileCleanupFailure,
bindArtifactFileCleanup,
artifactFileCancellationCause,
} from "../../artifacts/ArtifactFileOperation.js";
import { ArtifactResourceScope } from "../../artifacts/ArtifactResourceScope.js";
import { analysisErrorWithCleanupFailure } from "../../domain/analysisErrorCleanup.js";
import type { ElfNotesPort } from "../../application/binaryDiagnostics/ElfNotesPort.js";
import type { ExecutionOptions } from "../../application/AnalysisProvider.js";
import { ArtifactOperationError } from "../../domain/artifactOperationError.js";
import {
AnalysisAccessDeniedError,
AnalysisArtifactChangedError,
AnalysisCancelledError,
AnalysisInputError,
AnalysisResourceConstraintError,
} from "../../domain/analysisErrorCore.js";
import { fixedAllowanceOptions } from "../../domain/fixedAllowanceRemediation.js";
import {
MAX_INSPECT_ELF_NOTE_ENTRIES,
MAX_INSPECT_ELF_NOTE_FILE_BYTES,
type InspectElfNotesInput,
} from "../../domain/native/elfNotes.js";
import { err, ok } from "../../domain/result.js";
import { ProviderCleanupError } from "../../domain/providerCleanupError.js";
import { ELF_NOTES_PROVIDER } from "../../application/InvestigationProviders.js";
import { parseElfNotes } from "./ElfNoteParser.js";
const OPERATION = "inspect_elf_notes";
/** Portable parser over one bounded, identity-checked regular file. */
export class ElfNotesProvider implements ElfNotesPort {
readonly identity = ELF_NOTES_PROVIDER;
readonly #resources = new ArtifactResourceScope();
async close(): Promise<void> {
try {
await this.#resources.close();
} catch (cause: unknown) {
if (
!(cause instanceof ArtifactReaderFailure) ||
cause.cleanup === undefined
)
throw cause;
const error = new ProviderCleanupError(
ELF_NOTES_PROVIDER.id,
cause.cleanup.resources,
{ reason: cause.cleanup.reason },
{ cause, operation: OPERATION },
);
bindArtifactFileCleanup(cause, error);
throw error;
}
}
async inspect(input: InspectElfNotesInput, options?: ExecutionOptions) {
try {
return await this.#resources.run(async () => {
const snapshot = await readStableArtifact(
input.path,
input.max_file_bytes,
options?.signal,
undefined,
this.#resources,
);
return ok(
parseElfNotes(
snapshot.bytes,
{
path: input.path,
sha256: snapshot.sha256,
bytes: snapshot.bytes.length,
},
input,
),
);
});
} catch (cause) {
return err(elfNotesFailure(cause, input, options?.signal));
}
}
}
const NOTE_TEXT_BUDGET =
"The ELF note text, hash, and scan budget tied to the admitted image length";
const inputBudgetRemediation = (
boundary: "file-size" | "capacity",
input: InspectElfNotesInput,
): string =>
boundary === "file-size"
? input.max_file_bytes >= MAX_INSPECT_ELF_NOTE_FILE_BYTES
? `The image exceeds the ${String(MAX_INSPECT_ELF_NOTE_FILE_BYTES)}-byte input ceiling this operation accepts; select a smaller artifact.`
: `Raise max_file_bytes above ${String(input.max_file_bytes)} bytes or select a smaller artifact; the limit is the caller-selected input read budget.`
: input.max_entries >= MAX_INSPECT_ELF_NOTE_ENTRIES
? `The image exceeds the ${String(MAX_INSPECT_ELF_NOTE_ENTRIES)}-entry note-table ceiling this operation accepts; select a smaller artifact.`
: `Raise max_entries above ${String(input.max_entries)} or select a smaller artifact; the limit is the caller-selected note entry budget.`;
const elfNotesFailurePrimary = (
cause: unknown,
input: InspectElfNotesInput,
) => {
if (cause instanceof ArtifactReaderFailure && cause.reason === "integrity")
return new AnalysisArtifactChangedError(
OPERATION,
input.path,
cause.message,
{
cause,
},
);
if (cause instanceof ArtifactReaderFailure && cause.reason === "cancelled")
return new AnalysisCancelledError(OPERATION, {
cause: artifactFileCancellationCause(cause),
});
if (cause instanceof ArtifactReaderFailure) {
if (cause.reason === "limit") {
// Caller-selected read and entry budgets name the bound to raise.
// The note text, hash, and scan ceiling equals the admitted image
// length, so host memory cannot raise it.
const noteTextBudget = cause.boundary === "memory";
return new AnalysisResourceConstraintError(
OPERATION,
cause.boundary ?? "memory",
cause.message,
{
max_file_bytes: input.max_file_bytes,
max_entries: input.max_entries,
},
{
cause,
...(cause.boundary === undefined
? {}
: noteTextBudget
? fixedAllowanceOptions(NOTE_TEXT_BUDGET, "parser")
: {
retryable: false as const,
remediationAction: inputBudgetRemediation(
cause.boundary,
input,
),
}),
},
);
}
if (cause.reason === "path" || cause.reason === "format")
return new AnalysisInputError(OPERATION, { cause }, [
{
path: ["path"],
reason: "invalid_format",
message: cause.message,
},
]);
}
if (cause instanceof Error && "code" in cause) {
if (cause.code === "EACCES" || cause.code === "EPERM")
return new AnalysisAccessDeniedError(OPERATION, input.path, cause.code, {
cause,
});
if (
["ENOENT", "ENOTDIR", "ELOOP", "ENAMETOOLONG"].includes(
String(cause.code),
)
)
return new AnalysisInputError(OPERATION, { cause }, [
{
path: ["path"],
reason: "invalid_value",
message: `Selected ELF image could not be read (${String(cause.code)}): ${input.path}.`,
},
]);
}
return new ArtifactOperationError(
OPERATION,
cause instanceof ArtifactReaderFailure ? cause.reason : "io",
undefined,
cause instanceof Error ? cause.message : "ELF note inspection failed.",
{ cause },
);
};
const elfNotesFailure = (
cause: unknown,
input: InspectElfNotesInput,
signal: AbortSignal | undefined,
) => {
const primary =
cause instanceof ArtifactFileCleanupFailure &&
cause.fileCleanupFailure.outcome.kind === "failed"
? elfNotesFailurePrimary(cause.fileCleanupFailure.outcome.cause, input)
: cause instanceof ArtifactFileCleanupFailure && signal?.aborted
? new AnalysisCancelledError(OPERATION, { cause: signal.reason })
: elfNotesFailurePrimary(cause, input);
if (!(cause instanceof ArtifactReaderFailure) || cause.cleanup === undefined)
return primary;
const cleanup = new ProviderCleanupError(
ELF_NOTES_PROVIDER.id,
cause.cleanup.resources,
{ reason: cause.cleanup.reason },
{ operation: OPERATION, cause },
);
const error = analysisErrorWithCleanupFailure(primary, cleanup, OPERATION);
bindArtifactFileCleanup(cause, error);
return error;
};