mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(artifacts): split U-Boot script uImages on their size table (#7805)
U-Boot's mkimage writes the multi-file size table for script images
(`mkimage -T script`, the boot.scr format) exactly as it does for multi
images, `mkimage -l` lists the script as "Image 0", and the `source`
command skips the table before running the script ("scripts are just
multi-image files with one component", boot/image-board.c).
The uImage reader parsed the table only for IH_TYPE_MULTI, so a script
image reported sub_image_count 0 and exposed only payload.bin, whose
first bytes are the binary size table rather than the script text.
Treat IH_TYPE_SCRIPT like IH_TYPE_MULTI: parse the same bounded table,
expose image-<n>.bin components and report the size-table limitation.
payload.bin is unchanged. A script payload without a valid table now
fails closed, as a multi image already does.
This commit is contained in:
1 parent
44331d04d2
commit
f19436c156
5 files changed
+59
-7
No files matched your search
@@ -317,8 +317,8 @@ portable and requires no external tool. See
|
||||
The root facts record the 64-byte header (architecture, OS, type, and
|
||||
compression as raw values and names, load and entry addresses, image name,
|
||||
and timestamp) with the header and data CRC32 verified; the stored payload
|
||||
is extractable as `payload.bin`, and multi-file images additionally expose
|
||||
each sub-image as `image-<n>.bin` from the stored size table. Declared
|
||||
is extractable as `payload.bin`, and multi-file and script images additionally
|
||||
expose each sub-image as `image-<n>.bin` from the stored size table. Declared
|
||||
addresses are facts from the header, not proven entry points, and compressed
|
||||
payloads stay as stored. Intel HEX/S-record and FIT images are separate
|
||||
readers and are not covered here. See [U-Boot legacy uImage](uimage.md) for
|
||||
|
||||
+3
-2
@@ -15,8 +15,9 @@ The header's CRC32 and the declared payload's CRC32 must verify before a
|
||||
successful inventory. `payload.bin` contains the complete stored payload.
|
||||
Multi-file images also expose `image-0.bin`, `image-1.bin`, and so on. These
|
||||
component names are generated from table order. Components follow the zero-terminated big-endian size table, with four-byte
|
||||
padding after every nonfinal component. Their physical source ranges and
|
||||
content hashes retain precise provenance. Extraction writes the complete
|
||||
padding after every nonfinal component. Script images (`boot.scr`) carry the
|
||||
same table, so their script text is `image-0.bin`. Component physical source
|
||||
ranges and content hashes retain precise provenance. Extraction writes the complete
|
||||
payload and every component, so multi-file output can approach twice the input
|
||||
size. Compressed payloads remain compressed.
|
||||
|
||||
|
||||
@@ -266,7 +266,44 @@ it("splits multi images and keeps compressed payloads as stored", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("fails closed on corrupt CRCs, truncation, and bad multi tables", async () => {
|
||||
it("splits a script image's size table like a multi image", async () => {
|
||||
// mkimage writes the multi-file size table for scripts too, and U-Boot's
|
||||
// `source` skips it: "scripts are just multi-image files with one component".
|
||||
const script = Buffer.from("setenv bootargs console=ttyS0\nbootz\n");
|
||||
const payload = buildMultiPayload([script]);
|
||||
const path = await writeCarrier(
|
||||
"uimage-script",
|
||||
buildUImage({ type: 6, name: "boot script", payload }),
|
||||
);
|
||||
const snapshot = await scanCanonicalArtifactInventory(path);
|
||||
const root = snapshot.occurrences.find((row) => row.logical_path === ".");
|
||||
expect(root?.format_metadata).toMatchObject({
|
||||
type_name: "script",
|
||||
sub_image_count: 1,
|
||||
});
|
||||
expect(snapshot.limitations.join(" ")).toContain("size table");
|
||||
const reader = new UImageArtifactReader(path);
|
||||
try {
|
||||
const entries = [];
|
||||
for await (const item of reader.entries()) entries.push(item);
|
||||
const byPath = new Map(entries.map((item) => [item.path, item]));
|
||||
expect([...byPath.keys()].sort()).toEqual(["image-0.bin", "payload.bin"]);
|
||||
expect(
|
||||
(await collect(await reader.open(byPath.get("image-0.bin")!))).equals(
|
||||
script,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
(await collect(await reader.open(byPath.get("payload.bin")!))).equals(
|
||||
payload,
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
await reader.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("fails closed on corrupt CRCs, truncation, and bad size tables", async () => {
|
||||
const cases: [string, Buffer, RegExp][] = [
|
||||
["data CRC", buildUImage({ corruptDataCrc: true }), /data CRC32 mismatch/u],
|
||||
[
|
||||
@@ -287,6 +324,11 @@ it("fails closed on corrupt CRCs, truncation, and bad multi tables", async () =>
|
||||
}),
|
||||
/no zero terminator/u,
|
||||
],
|
||||
[
|
||||
"script without size table",
|
||||
buildUImage({ type: 6, payload: Buffer.from("echo hi\n") }),
|
||||
/no zero terminator/u,
|
||||
],
|
||||
];
|
||||
for (const [label, bytes, pattern] of cases) {
|
||||
const path = await writeCarrier(
|
||||
|
||||
@@ -21,6 +21,7 @@ import { ArtifactResourceScope } from "./ArtifactResourceScope.js";
|
||||
import { readFileHandleChunks } from "../filesystem/readFileHandleChunks.js";
|
||||
import type { StableRegularFileDescriptor } from "../filesystem/RegularFile.js";
|
||||
import {
|
||||
hasUImageSizeTable,
|
||||
parseMultiImageTable,
|
||||
parseUImageHeaderFields,
|
||||
UIMAGE_HEADER_SIZE,
|
||||
@@ -82,7 +83,7 @@ export class UImageArtifactReader implements ArtifactReader {
|
||||
options?.onCoverage?.({
|
||||
status: "complete",
|
||||
limitations:
|
||||
this.#header?.type_name === "multi"
|
||||
this.#header !== undefined && hasUImageSizeTable(this.#header.type)
|
||||
? [
|
||||
"Multi-image sub-images are split from the stored size table; the payload entry retains the full stored bytes.",
|
||||
]
|
||||
@@ -216,7 +217,7 @@ export class UImageArtifactReader implements ArtifactReader {
|
||||
let dataCrc32 = 0;
|
||||
let read = 0;
|
||||
let subImages: readonly UImageSubImage[] = [];
|
||||
if (header.type === 4) {
|
||||
if (hasUImageSizeTable(header.type)) {
|
||||
const tablePrefix = await readArtifactFileBytes(
|
||||
handle,
|
||||
UIMAGE_HEADER_SIZE,
|
||||
|
||||
@@ -17,6 +17,14 @@ export const UIMAGE_MAX_SUB_IMAGES = 4096;
|
||||
/** Bytes of payload prefix that can hold a maximal multi-image size table. */
|
||||
export const UIMAGE_MULTI_TABLE_WINDOW = (UIMAGE_MAX_SUB_IMAGES + 1) * 4;
|
||||
|
||||
/**
|
||||
* Multi (4) and script (6) payloads begin with the size table: mkimage writes
|
||||
* it for both, and U-Boot's `source` skips it because "scripts are just
|
||||
* multi-image files with one component".
|
||||
*/
|
||||
export const hasUImageSizeTable = (type: number): boolean =>
|
||||
type === 4 || type === 6;
|
||||
|
||||
export interface UImageSubImage {
|
||||
readonly index: number;
|
||||
/** Byte offset of the sub-image payload relative to the carrier start. */
|
||||
|
||||
Reference in new issue
Block a user