fix(firmware): exclude UF2 payloads overlapping MD5 metadata (#9593)

* fix(artifacts): exclude UF2 payloads overlapping MD5 metadata

* fix(artifacts): clarify DfuSe overlap ordering
This commit is contained in:
morluto authored and GitHub committed 2026-11-09 19:34:04 +08:00
1 parent 8358cfcab3
commit 9536ebb7ca
4 files changed
+84 -11

No files matched your search

+7 -7
View File
@@ -127,13 +127,13 @@ its declaration, so the same bytes moved to another device memory are
Root `format_metadata` (`dfuse`) records:
| Field | Meaning |
| ---------------------------------------------------- | ------------------------------------------------------------------------------- |
| `image_size`, `target_count` | `DFUImageSize` and `bTargets` from the prefix |
| `element_count`, `empty_element_count`, `data_bytes` | All elements, elements of 0 bytes (which have no entry), and element data bytes |
| `overlapping_element_count` | Elements that overlap an earlier element of the same alternate setting |
| `device_version`, `product_id`, `vendor_id` | `bcdDevice`, `idProduct` and `idVendor` from the suffix |
| `suffix_crc`, `computed_crc` | The stored `dwCRC`, and the same CRC computed over every byte before it |
| Field | Meaning |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `image_size`, `target_count` | `DFUImageSize` and `bTargets` from the prefix |
| `element_count`, `empty_element_count`, `data_bytes` | All elements, elements of 0 bytes (which have no entry), and element data bytes |
| `overlapping_element_count` | Elements that overlap an earlier element in address order within the same alternate setting |
| `device_version`, `product_id`, `vendor_id` | `bcdDevice`, `idProduct` and `idVendor` from the suffix |
| `suffix_crc`, `computed_crc` | The stored `dwCRC`, and the same CRC computed over every byte before it |
Recognition already requires `bVersion` 1 and `bcdDFU` `0x011A`, so they are
not repeated. The DFU suffix CRC is CRC-32 without the final inversion, as
+1 -1
View File
@@ -212,7 +212,7 @@ const parseDfuSe = (
...(overlaps.length === 0
? []
: [
`${String(overlaps.length)} DfuSe element(s) overlap an earlier element of the same alternate setting; each stays a separate entry and no bytes were merged: ${named(overlaps)}.`,
`${String(overlaps.length)} DfuSe element(s) overlap an element earlier in address order within the same alternate setting; each stays a separate entry and no bytes were merged: ${named(overlaps)}.`,
]),
...(walk.unlistedCount === 0
? []
+7 -3
View File
@@ -8,6 +8,7 @@ import { UF2_FAMILY_REGISTRY, uf2FamilyName } from "./uf2Families.js";
export const UF2_BLOCK_BYTES = 512;
const UF2_DATA_OFFSET = 32;
const UF2_DATA_BYTES = 476;
const UF2_MD5_BYTES = 24;
const UF2_MAGIC_START0 = 0x0a32_4655;
const UF2_MAGIC_START1 = 0x9e5d_5157;
const UF2_MAGIC_END = 0x0ab1_6f30;
@@ -236,9 +237,12 @@ class StreamingUf2Decoder implements RegionDecoder<Uf2Image> {
? "end magic number does not match"
: size > UF2_DATA_BYTES
? `payload size ${String(size)} exceeds the ${String(UF2_DATA_BYTES)}-byte data area`
: address + size > ADDRESS_SPACE
? `payload at ${hex32(address)} with ${String(size)} bytes wraps past 0xffffffff`
: undefined;
: (flags & UF2_FLAG_MD5) !== 0 &&
size > UF2_DATA_BYTES - UF2_MD5_BYTES
? "payload overlaps the reserved MD5 checksum metadata"
: address + size > ADDRESS_SPACE
? `payload at ${hex32(address)} with ${String(size)} bytes wraps past 0xffffffff`
: undefined;
if (fault !== undefined) {
this.#invalid += 1;
this.#firstInvalid ??= { block: index, reason: fault };
@@ -14,6 +14,7 @@ import { artifactInspectionResultSchema } from "../../../src/domain/artifactInsp
import { dfuseFile } from "../../../src/domain/firmware/dfuse.fixture.js";
import {
RP2040_FAMILY,
UF2_FLAGS,
RP2XXX_ABSOLUTE_FAMILY,
patternBytes,
uf2Block,
@@ -113,6 +114,74 @@ it.each(["full", "compact"] as const)(
},
);
it("preserves valid regions while excluding a UF2 payload overlapping MD5 metadata", async () => {
const root = await createTestTempDirectory("rea-uf2-md5-mcp-");
const path = join(root, "firmware.uf2");
const valid = patternBytes(452, 1);
const plain = patternBytes(476, 2);
await writeFile(
path,
uf2File([
uf2Block({
address: 0x1000,
payload: valid,
blockNo: 0,
numBlocks: 3,
flags: UF2_FLAGS.md5,
}),
uf2Block({
address: 0x2000,
payload: patternBytes(453, 3),
blockNo: 1,
numBlocks: 3,
flags: UF2_FLAGS.md5,
}),
uf2Block({ address: 0x3000, payload: plain, blockNo: 2, numBlocks: 3 }),
]),
);
const { call } = await connectLocalToolsMcp({});
let outputRoot: string | undefined;
try {
const opened = await call("open_binary", { path });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const inspected = await call("inspect_artifact", {});
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
const inspection = artifactInspectionResultSchema.parse(
parseEvidence(inspected.structuredContent).normalized_result,
);
expect(inspection.coverage.status).toBe("partial");
expect(inspection.limitations).toContainEqual(
expect.stringMatching(/invalid.*block 1.*MD5/iu),
);
const extracted = await call("extract_artifact", {});
expect(extracted.isError, JSON.stringify(extracted)).not.toBe(true);
const result = artifactExtractionResultSchema.parse(
parseEvidence(extracted.structuredContent).normalized_result,
);
outputRoot = result.output_root;
expect(result.coverage.status).toBe("unknown");
expect(
Buffer.compare(
await readFile(join(outputRoot, "no-family/0x00001000-0x000011c3.bin")),
valid,
),
).toBe(0);
expect(
Buffer.compare(
await readFile(join(outputRoot, "no-family/0x00003000-0x000031db.bin")),
plain,
),
).toBe(0);
await expect(
readFile(join(outputRoot, "no-family/0x00002000-0x000021c4.bin")),
).rejects.toMatchObject({ code: "ENOENT" });
await call("close_binary", {});
} finally {
if (outputRoot !== undefined)
await rm(outputRoot, { recursive: true, force: true });
}
});
it("delivers a DfuSe CRC contradiction under both integrity policies through MCP", async () => {
const root = await createTestTempDirectory("rea-dfuse-mcp-");
const path = join(root, "firmware.dfu");