mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 18:52:48 +00:00
fix(native): scale the NativeAOT work budget with the admitted image
The parser's work budget was a fixed 64 Mi units, so a real 26 MB .NET 8 NativeAOT image (bflat 8.0.2) stopped frozen-string recovery at 4,433 of 6,685 strings and reported partial. Give each image a base budget plus 8 units per admitted source byte, bounded by the source-byte ceiling, and record the scaling in the analysis profile (contract revision v11). With it, that image completes with all 6,685 frozen strings using 71.6 M units. Only the budget scaling of #4559 is taken: main already resolves the base-type closure in linear time. Fixes #4445 Supersedes #4559 Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
1 parent
79097f52b2
commit
c8d5413d0f
4 files changed
+78
-23
No files matched your search
@@ -2,7 +2,11 @@ import { describe, expect, it } from "vitest";
|
||||
import type { NativeAotParserLimits } from "./nativeAotPe.js";
|
||||
import { AnalysisResourceConstraintError } from "../analysisErrorCore.js";
|
||||
import {
|
||||
DEFAULT_NATIVE_AOT_PARSER_LIMITS,
|
||||
NATIVE_AOT_BASE_WORK_UNITS,
|
||||
NATIVE_AOT_MAX_REPORT_BYTES,
|
||||
NATIVE_AOT_MAX_SOURCE_BYTES,
|
||||
nativeAotParserLimitsFor,
|
||||
parseNativeAotPe,
|
||||
} from "./nativeAotPe.js";
|
||||
import {
|
||||
@@ -12,6 +16,34 @@ import {
|
||||
|
||||
const REPORT_BUDGET = NATIVE_AOT_MAX_REPORT_BYTES;
|
||||
|
||||
describe("NativeAOT work budget scaling", () => {
|
||||
it("grows with the admitted image and stops at the source-byte ceiling", () => {
|
||||
const small = nativeAotParserLimitsFor(1024);
|
||||
const large = nativeAotParserLimitsFor(26_180_096);
|
||||
expect(small.workUnits).toBeGreaterThan(NATIVE_AOT_BASE_WORK_UNITS);
|
||||
// bflat 8.0.2's 26,180,096-byte image needs 71,645,819 units to complete.
|
||||
expect(large.workUnits).toBeGreaterThan(71_645_819);
|
||||
expect(nativeAotParserLimitsFor(Number.MAX_SAFE_INTEGER)).toEqual(
|
||||
DEFAULT_NATIVE_AOT_PARSER_LIMITS,
|
||||
);
|
||||
expect(DEFAULT_NATIVE_AOT_PARSER_LIMITS.sourceBytes).toBe(
|
||||
NATIVE_AOT_MAX_SOURCE_BYTES,
|
||||
);
|
||||
});
|
||||
|
||||
it("charges an image against the budget for its own size by default", () => {
|
||||
const bytes = nativeAotPeFixture();
|
||||
const report = parseNativeAotPe(
|
||||
bytes,
|
||||
nativeAotPeDigest(bytes),
|
||||
REPORT_BUDGET,
|
||||
).summary;
|
||||
expect(report.coverage.work_budget_units).toBe(
|
||||
nativeAotParserLimitsFor(bytes.length).workUnits,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("NativeAOT parser resource budgets", () => {
|
||||
it("reports source-cap rejection without hashing or inventing omitted-byte coverage", () => {
|
||||
const bytes = nativeAotPeFixture();
|
||||
|
||||
@@ -26,22 +26,39 @@ const FROZEN_OBJECT_REGION = 206;
|
||||
export const NATIVE_AOT_MAX_SOURCE_BYTES = 128 * 1024 * 1024;
|
||||
/** Bound one parser-produced JSON report independently of any transport. */
|
||||
export const NATIVE_AOT_MAX_REPORT_BYTES = 4 * 1024 * 1024;
|
||||
export const NATIVE_AOT_MAX_WORK_UNITS = 64 * 1024 * 1024;
|
||||
/** Work every image may use, covering headers, directories and sections. */
|
||||
export const NATIVE_AOT_BASE_WORK_UNITS = 64 * 1024 * 1024;
|
||||
/**
|
||||
* Work added per admitted source byte, so discovery is not starved on larger
|
||||
* images. bflat 8.0.2's 26,180,096-byte win-x64 NativeAOT image completes with
|
||||
* 71,645,819 units (about 2.7 per byte), past the former fixed 64 Mi budget.
|
||||
*/
|
||||
export const NATIVE_AOT_WORK_UNITS_PER_SOURCE_BYTE = 8;
|
||||
export const NATIVE_AOT_MAX_WORKING_MEMORY_BYTES = 64 * 1024 * 1024;
|
||||
/** Version of the built-in read-only PE metadata interpretation in profiles. */
|
||||
export const NATIVE_AOT_PROFILE_CONTRACT_REVISION =
|
||||
"rtr-9.1-x64-pe-read-only-v10";
|
||||
"rtr-9.1-x64-pe-read-only-v11";
|
||||
|
||||
export type NativeAotParserLimits = {
|
||||
readonly sourceBytes: number;
|
||||
readonly workUnits: number;
|
||||
readonly workingMemoryBytes: number;
|
||||
};
|
||||
export const DEFAULT_NATIVE_AOT_PARSER_LIMITS: NativeAotParserLimits = {
|
||||
/** Limits for one admitted image; the work budget follows its size. */
|
||||
export const nativeAotParserLimitsFor = (
|
||||
sourceBytes: number,
|
||||
): NativeAotParserLimits => ({
|
||||
sourceBytes: NATIVE_AOT_MAX_SOURCE_BYTES,
|
||||
workUnits: NATIVE_AOT_MAX_WORK_UNITS,
|
||||
workUnits:
|
||||
NATIVE_AOT_BASE_WORK_UNITS +
|
||||
Math.min(Math.max(sourceBytes, 0), NATIVE_AOT_MAX_SOURCE_BYTES) *
|
||||
NATIVE_AOT_WORK_UNITS_PER_SOURCE_BYTE,
|
||||
workingMemoryBytes: NATIVE_AOT_MAX_WORKING_MEMORY_BYTES,
|
||||
};
|
||||
});
|
||||
|
||||
/** The scaled limits at the source-byte ceiling. */
|
||||
export const DEFAULT_NATIVE_AOT_PARSER_LIMITS: NativeAotParserLimits =
|
||||
nativeAotParserLimitsFor(NATIVE_AOT_MAX_SOURCE_BYTES);
|
||||
|
||||
class NativeAotParserBudget {
|
||||
workUnits = 0;
|
||||
@@ -1395,7 +1412,7 @@ export const parseNativeAotPe = (
|
||||
expectedSha256: string,
|
||||
maximumSummaryBytes: number,
|
||||
loadedImageBase?: string,
|
||||
limits = DEFAULT_NATIVE_AOT_PARSER_LIMITS,
|
||||
limits = nativeAotParserLimitsFor(bytes.byteLength),
|
||||
): NativeAotPeResult => {
|
||||
if (!Number.isSafeInteger(maximumSummaryBytes) || maximumSummaryBytes < 0)
|
||||
throw new AnalysisInputError("parse_native_aot_pe", undefined, [
|
||||
|
||||
@@ -48,7 +48,8 @@ import {
|
||||
NATIVE_AOT_MAX_REPORT_BYTES,
|
||||
NATIVE_AOT_MAX_SOURCE_BYTES,
|
||||
NATIVE_AOT_MAX_WORKING_MEMORY_BYTES,
|
||||
NATIVE_AOT_MAX_WORK_UNITS,
|
||||
NATIVE_AOT_BASE_WORK_UNITS,
|
||||
NATIVE_AOT_WORK_UNITS_PER_SOURCE_BYTE,
|
||||
NATIVE_AOT_PROFILE_CONTRACT_REVISION,
|
||||
} from "../domain/native/nativeAotPe.js";
|
||||
|
||||
@@ -301,7 +302,9 @@ export const resolveGhidraAnalysisProfile = async (
|
||||
native_aot_metadata: {
|
||||
contract_revision: NATIVE_AOT_PROFILE_CONTRACT_REVISION,
|
||||
source_bytes: NATIVE_AOT_MAX_SOURCE_BYTES,
|
||||
work_units: NATIVE_AOT_MAX_WORK_UNITS,
|
||||
base_work_units: NATIVE_AOT_BASE_WORK_UNITS,
|
||||
work_units_per_source_byte:
|
||||
NATIVE_AOT_WORK_UNITS_PER_SOURCE_BYTE,
|
||||
working_memory_bytes: NATIVE_AOT_MAX_WORKING_MEMORY_BYTES,
|
||||
report_bytes: NATIVE_AOT_MAX_REPORT_BYTES,
|
||||
},
|
||||
|
||||
@@ -883,6 +883,21 @@ describe("Ghidra platform support", () => {
|
||||
).toBe("read-only-relocation-limitations-v1");
|
||||
});
|
||||
|
||||
it("keeps Windows annotation mutation unavailable independently of native controls", () => {
|
||||
const ghidra = provider(installationHost("win32"));
|
||||
expect(
|
||||
ghidra
|
||||
.capabilities()
|
||||
.find(({ operation }) => operation === "annotate_native_function"),
|
||||
).toMatchObject({
|
||||
available: false,
|
||||
reason: "Windows Ghidra P0 does not admit database mutation.",
|
||||
availabilityCode: "unsupported_host",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ghidra PE analysis profile identity", () => {
|
||||
it("commits the PE role and managed classification to profile identity", async () => {
|
||||
const ghidra = provider(installationHost("win32"));
|
||||
const application = peTarget("x86_64");
|
||||
@@ -904,9 +919,10 @@ describe("Ghidra platform support", () => {
|
||||
executable_role: target.executableRole,
|
||||
managed: target.managed,
|
||||
native_aot_metadata: {
|
||||
contract_revision: "rtr-9.1-x64-pe-read-only-v10",
|
||||
contract_revision: "rtr-9.1-x64-pe-read-only-v11",
|
||||
source_bytes: 128 * 1024 * 1024,
|
||||
work_units: 64 * 1024 * 1024,
|
||||
base_work_units: 64 * 1024 * 1024,
|
||||
work_units_per_source_byte: 8,
|
||||
working_memory_bytes: 64 * 1024 * 1024,
|
||||
report_bytes: 4 * 1024 * 1024,
|
||||
},
|
||||
@@ -923,19 +939,6 @@ describe("Ghidra platform support", () => {
|
||||
"native_aot_metadata",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps Windows annotation mutation unavailable independently of native controls", () => {
|
||||
const ghidra = provider(installationHost("win32"));
|
||||
expect(
|
||||
ghidra
|
||||
.capabilities()
|
||||
.find(({ operation }) => operation === "annotate_native_function"),
|
||||
).toMatchObject({
|
||||
available: false,
|
||||
reason: "Windows Ghidra P0 does not admit database mutation.",
|
||||
availabilityCode: "unsupported_host",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ghidra Windows DOS admission", () => {
|
||||
|
||||
Reference in new issue
Block a user