fix(go): reuse PE content spans for build metadata search
This commit is contained in:
1 parent
cac12ae334
commit
266dd1d958
4 files changed
+125
-1
No files matched your search
@@ -0,0 +1,63 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { readGoBinaryImage } from "./GoBinaryImage.js";
|
||||
import { readGoPeImage } from "./GoPeImage.js";
|
||||
import {
|
||||
createGoBinaryFixture,
|
||||
setGoPeVirtualSize,
|
||||
} from "../../tests/fixtures/go/image.js";
|
||||
|
||||
it.each([32, 64] as const)(
|
||||
"uses raw-size fallback for PE%d Go build-info and keeps RVA mappings intact",
|
||||
(bits) => {
|
||||
for (const encoding of ["inline", "pointer"] as const) {
|
||||
const fixture = createGoBinaryFixture({ format: "pe", bits, encoding });
|
||||
const expected = readGoBinaryImage(fixture.bytes).build_info;
|
||||
expect(expected).not.toBeNull();
|
||||
setGoPeVirtualSize(fixture.bytes, 0);
|
||||
|
||||
const container = readGoPeImage(fixture.bytes);
|
||||
expect(container.sections[0]).toMatchObject({
|
||||
name: ".data",
|
||||
offset: 1024,
|
||||
size: 2048,
|
||||
file_backed: true,
|
||||
});
|
||||
expect(container.mappings[0]).toMatchObject({
|
||||
address: 0x10400n,
|
||||
offset: 1024,
|
||||
size: 2048,
|
||||
});
|
||||
expect(container.search[0]).toMatchObject({
|
||||
address: 0x10400n,
|
||||
offset: 1024,
|
||||
size: 2048,
|
||||
});
|
||||
expect(readGoBinaryImage(fixture.bytes).build_info).toEqual(expected);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["inline", "pointer"] as const)(
|
||||
"does not search PE raw alignment padding beyond a nonzero VirtualSize (%s)",
|
||||
(encoding) => {
|
||||
const fixture = createGoBinaryFixture({ format: "pe", bits: 64, encoding });
|
||||
// Put an otherwise valid, aligned record in the raw-only tail.
|
||||
fixture.bytes.copy(
|
||||
fixture.bytes,
|
||||
fixture.headerOffset + 512,
|
||||
fixture.headerOffset,
|
||||
fixture.headerOffset + 512,
|
||||
);
|
||||
fixture.bytes.fill(0, fixture.headerOffset, fixture.headerOffset + 512);
|
||||
setGoPeVirtualSize(fixture.bytes, 512);
|
||||
|
||||
const container = readGoPeImage(fixture.bytes);
|
||||
expect(container.sections[0]).toMatchObject({
|
||||
size: 512,
|
||||
file_backed: true,
|
||||
});
|
||||
expect(container.mappings[0]).toMatchObject({ size: 2048, offset: 1024 });
|
||||
expect(container.search[0]).toMatchObject({ size: 512, offset: 1024 });
|
||||
expect(readGoBinaryImage(fixture.bytes).build_info).toBeNull();
|
||||
},
|
||||
);
|
||||
+1
-1
@@ -137,7 +137,7 @@ export const readGoPeImage = (bytes: Buffer): GoBinaryContainer => {
|
||||
size !== 0 &&
|
||||
(characteristics & ~0x00600000) >>> 0 === 0xc0000040
|
||||
) {
|
||||
search = { ...mapping, size: Math.min(size, virtualSize) };
|
||||
search = { ...mapping, size: contentSize };
|
||||
}
|
||||
}
|
||||
const machine = reader.u16(pe + 4);
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
createGoSplitElfFixture,
|
||||
createGoOverlappingElfFixture,
|
||||
createGoBinaryPathFailureFixture,
|
||||
setGoPeVirtualSize,
|
||||
GO_MODULE_TEXT,
|
||||
} from "../fixtures/go/image.js";
|
||||
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
||||
@@ -76,6 +77,56 @@ cliTest(
|
||||
30000,
|
||||
);
|
||||
|
||||
for (const bits of [32, 64] as const) {
|
||||
for (const encoding of ["inline", "pointer"] as const) {
|
||||
cliTest(
|
||||
`preserves PE ${bits}-bit ${encoding} build info at zero VirtualSize through CLI and stdio MCP`,
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-go-pe-zero-vsize-");
|
||||
const fixture = createGoBinaryFixture({ format: "pe", bits, encoding });
|
||||
const ordinaryBytes = Buffer.from(fixture.bytes);
|
||||
const zeroSizeBytes = Buffer.from(fixture.bytes);
|
||||
setGoPeVirtualSize(zeroSizeBytes, 0);
|
||||
const { client, call } = await connectLocalToolsMcp();
|
||||
const buildInfos = [];
|
||||
|
||||
for (const [label, bytes] of [
|
||||
["ordinary", ordinaryBytes],
|
||||
["zero-vsize", zeroSizeBytes],
|
||||
] as const) {
|
||||
const path = join(root, `${label}.exe`);
|
||||
await writeFile(path, bytes);
|
||||
const result = await cli.run({
|
||||
arguments: ["inspect-go-binary", path, "--json"],
|
||||
});
|
||||
expect(result.exitCode, result.stderr).toBe(0);
|
||||
const evidence = toolContract("inspect_go_binary").outputSchema.parse(
|
||||
result.json,
|
||||
);
|
||||
expect(evidence.subject?.digest.sha256).toBe(
|
||||
createHash("sha256").update(bytes).digest("hex"),
|
||||
);
|
||||
expect(evidence.normalized_result.build_info).toMatchObject({
|
||||
go_version: "go1.26.0",
|
||||
encoding,
|
||||
header_offset: fixture.headerOffset,
|
||||
module_text: GO_MODULE_TEXT,
|
||||
});
|
||||
buildInfos.push(evidence.normalized_result.build_info);
|
||||
|
||||
const response = await call("inspect_go_binary", { path });
|
||||
expect(response.isError, JSON.stringify(response)).not.toBe(true);
|
||||
expect(response.structuredContent).toEqual(evidence);
|
||||
}
|
||||
|
||||
expect(buildInfos[1]).toEqual(buildInfos[0]);
|
||||
await client.ping();
|
||||
},
|
||||
30000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const expectedErrno of ["ELOOP", "ENAMETOOLONG"] as const) {
|
||||
cliTest.skipIf(expectedErrno === "ELOOP" && process.platform === "win32")(
|
||||
`reports native ${expectedErrno} path failure as invalid selected-path input through the public Go CLI`,
|
||||
|
||||
Vendored
+10
@@ -135,6 +135,16 @@ export const createGoBinaryFixture = (
|
||||
return { bytes, headerOffset, versionOffset, moduleOffset, moduleBytes };
|
||||
};
|
||||
|
||||
/** Set the first PE section's declared virtual content size in an authored fixture. */
|
||||
export const setGoPeVirtualSize = (
|
||||
bytes: Buffer,
|
||||
virtualSize: number,
|
||||
): void => {
|
||||
const pe = bytes.readUInt32LE(60);
|
||||
const section = pe + 24 + bytes.readUInt16LE(pe + 20);
|
||||
bytes.writeUInt32LE(virtualSize, section + 8);
|
||||
};
|
||||
|
||||
/** Two writable ELF loads, as in a PIE whose build info is in the later load. */
|
||||
export const createGoSplitElfFixture = (
|
||||
options: Parameters<typeof createGoBinaryFixture>[0] = {},
|
||||
|
||||
Reference in new issue
Block a user