fix(ghidra): read UTF-8 signed language definitions (#2772)
Buffer#toString kept a leading UTF-8 byte order mark as U+FEFF, so the strict XML parser rejected a valid .ldefs file as content outside the root element and aborted the whole language catalog read. XML 1.0 section 4.3.3 defines the mark as an encoding signature; decode with TextDecoder, which consumes it. Malformed XML is still rejected. Fixes #2533 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8823c55b0c
commit
ec4f4ddc58
|
|
@ -75,8 +75,10 @@ export const readGhidraLanguageCatalog = async (
|
|||
if (!entry.endsWith(".ldefs")) continue;
|
||||
const bytes = await readFile(join(directory, entry));
|
||||
if (bytes.length > MAX_LDEFS_BYTES) continue;
|
||||
// XML 1.0 section 4.3.3: a leading UTF-8 BOM is an encoding signature,
|
||||
// not document content; TextDecoder consumes it, Buffer#toString does not.
|
||||
definitions.push(
|
||||
...parseGhidraLanguageDefinitions(bytes.toString("utf8")),
|
||||
...parseGhidraLanguageDefinitions(new TextDecoder().decode(bytes)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
||||
import { parseConfig } from "../config/parseConfig.js";
|
||||
import type { BinaryTarget } from "../domain/binaryTargetTypes.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
|
|
@ -190,6 +191,28 @@ describe("Ghidra language catalog", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("reads a UTF-8 signed definition beside an unsigned sibling", async () => {
|
||||
const root = await createTestTempDirectory("rea-ghidra-bom-");
|
||||
const module = (name: string) =>
|
||||
join(root, "Ghidra/Processors", name, "data/languages");
|
||||
await mkdir(module("probe"), { recursive: true });
|
||||
await mkdir(module("x86"), { recursive: true });
|
||||
await writeFile(
|
||||
join(module("probe"), "probe.ldefs"),
|
||||
Buffer.concat([
|
||||
Buffer.from([0xef, 0xbb, 0xbf]),
|
||||
Buffer.from(
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n<language_definitions><language id="probe:LE:32:default"/></language_definitions>',
|
||||
),
|
||||
]),
|
||||
);
|
||||
await writeFile(join(module("x86"), "x86.ldefs"), X86_LDEFS);
|
||||
expect(await readGhidraLanguageCatalog(root)).toEqual([
|
||||
{ id: "probe:LE:32:default", compilerSpecIds: [] },
|
||||
...parseGhidraLanguageDefinitions(X86_LDEFS),
|
||||
]);
|
||||
});
|
||||
|
||||
it("admits only languages and compiler specs the installation defines", () => {
|
||||
const catalog = parseGhidraLanguageDefinitions(X86_LDEFS);
|
||||
expect(
|
||||
|
|
|
|||
Loading…
Reference in New Issue