ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
gen_jpeg_fixture.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: MIT
3# Copyright (c) 2026 Brighton Sikarskie
4"""Emit a minimal baseline JPEG to seed the libFuzzer corpus.
5
6This generator only needs to satisfy the parser in libs/ra8_hal/src/
7ra8_jpeg_sw.c well enough for the fuzzer to start from real coverage:
8valid SOI, valid SOF0 with parseable WxH, and either valid entropy
9bytes or an EOI marker right after SOS. The generator does NOT have
10to produce a visually decodable image.
11
12Usage:
13 python3 scripts/gen/gen_jpeg_fixture.py --width 8 --height 8 -o seed.jpg
14"""
15
16import argparse
17import struct
18import sys
19from pathlib import Path
20
21# JPEG SOF0 dimension field is a 16-bit unsigned integer (0x0001..0xFFFF).
22JPEG_DIM_MAX = 0xFFFF
23JPEG_DIM_MIN = 1
24
25
26def build_minimal_jpeg(width: int, height: int) -> bytes:
27 """Build a minimal baseline JPEG with the requested SOF0 dimensions.
28
29 The DCT coefficients in the entropy segment are not meaningful. This
30 is a parser-coverage seed only.
31 """
32 if not (JPEG_DIM_MIN <= width <= JPEG_DIM_MAX) or not (JPEG_DIM_MIN <= height <= JPEG_DIM_MAX):
33 msg = "width/height must be in 1..65535"
34 raise ValueError(msg)
35
36 out = bytearray()
37
38 # SOI
39 out += b"\xff\xd8"
40
41 # APP0 / JFIF 1.01
42 app0 = b"JFIF\x00" + b"\x01\x01" + b"\x00" + b"\x00\x01\x00\x01" + b"\x00\x00"
43 out += b"\xff\xe0" + struct.pack(">H", 2 + len(app0)) + app0
44
45 # DQT (one 8-bit luma table, all 1s)
46 dqt = b"\x00" + (b"\x01" * 64)
47 out += b"\xff\xdb" + struct.pack(">H", 2 + len(dqt)) + dqt
48
49 # SOF0 (baseline, 8-bit, single Y component)
50 sof0 = (
51 b"\x08" + struct.pack(">H", height) + struct.pack(">H", width) + b"\x01" + b"\x01\x11\x00"
52 )
53 out += b"\xff\xc0" + struct.pack(">H", 2 + len(sof0)) + sof0
54
55 # DHT (DC luma table, minimal valid)
56 dht_dc = b"\x00" + bytes([0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]) + bytes(range(12))
57 out += b"\xff\xc4" + struct.pack(">H", 2 + len(dht_dc)) + dht_dc
58
59 # DHT (AC luma table, minimal valid)
60 dht_ac = (
61 b"\x10" + bytes([0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7D]) + bytes(range(0xA2))
62 )
63 out += b"\xff\xc4" + struct.pack(">H", 2 + len(dht_ac)) + dht_ac
64
65 # SOS (single component)
66 sos = b"\x01" + b"\x01\x00" + b"\x00\x3f\x00"
67 out += b"\xff\xda" + struct.pack(">H", 2 + len(sos)) + sos
68
69 # Entropy segment -- a few bytes of plausible-looking coefficients.
70 # 0xFF in entropy must be followed by 0x00 (byte-stuffing). We avoid
71 # 0xFF entirely to keep this trivially valid.
72 out += bytes([0x00] * 16)
73
74 # EOI
75 out += b"\xff\xd9"
76
77 return bytes(out)
78
79
80def main() -> int:
81 """Emit one baseline JPEG of the requested size to stdout or a file.
82
83 Defaults to stdout (``-``) so the fixture can be piped straight into a
84 corpus directory without a temporary file.
85 """
86 parser = argparse.ArgumentParser(
87 description="Emit a minimal baseline JPEG seed for the libFuzzer corpus."
88 )
89 parser.add_argument("--width", type=int, default=8)
90 parser.add_argument("--height", type=int, default=8)
91 parser.add_argument("-o", "--output", default="-", help="Output file (default: stdout).")
92 args = parser.parse_args()
93
94 blob = build_minimal_jpeg(args.width, args.height)
95 if args.output == "-":
96 sys.stdout.buffer.write(blob)
97 else:
98 with Path(args.output).open("wb") as fh:
99 fh.write(blob)
100 return 0
101
102
103if __name__ == "__main__":
104 sys.exit(main())
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298