jpeg/jpeg-markers
version 1.0
JPEG at the marker level (APPn, COM, DQT, DHT, DRI, SOF0-2, SOS); tables and scan data are random, so files rarely decode
- Format
- JPEG File Interchange Format (jpeg)
- Authors
- The Fandango Fuzzer Team
- License
- Apache-2.0
- Fandango
- >=1.3
- Extensions
- jpg, jpeg
- Media type
- image/jpeg
- Specification
- https://www.w3.org/Graphics/JPEG/itu-t81.pdf
- Its files should decode
- rarely
- In the registry
- specs/jpeg/jpeg-markers/jpeg-markers.fan
Use it
fanbase install jpeg-markers fandango fuzz -F jpeg-markers -n 10
How it does
accepts 9%, covers 20.0%, 14/s. 1000 inputs, seed 1.
| Parser | Accepts | Version |
|---|---|---|
| djpeg | 0% | libjpeg-turbo version 2.1.5 (build 20250503) |
| ffmpeg | 9% | ffmpeg version 7.1.5-0+deb13u1 |
| imagemagick | 0% | Version: ImageMagick 7.1.1-43 Q16 x86_64 22550 |
| libjpeg-turbo-cov | 0% | libjpeg-turbo 3.2.0 |
| pillow | 0% | 12.3.0 |
The spec says its files decode rarely: as expected.
libjpeg-turbo-cov
- lines reached
- 20.0% of 5172
- real files reach
- 38.8%
- only the generated files
- 47 lines
- only the real files
- 1023 lines
Cite it
@software{fanbase_jpeg_jpeg_markers_1_0,
author = {{The Fandango Fuzzer Team}},
title = {JPEG File Interchange Format (jpeg/jpeg-markers), a Fandango input specification},
version = {1.0},
url = {https://github.com/fandango-fuzzer/fanbase},
note = {Fanbase registry, specs/jpeg/jpeg-markers/jpeg-markers.fan, sha256 abf90cf8d0c8},
}
Source
Source (114 lines)
# JPEG/JFIF at the marker level: SOI, JFIF, other APPn, COM, DQT, DHT, DRI, a
# frame header (SOF0, SOF1 or SOF2, one or three components), SOS, entropy
# coded data with restart markers, EOI.
#
# Every segment shows up in every file and each length field matches its
# payload. Tables and scan data are random bytes, so the files rarely decode.
<start> ::= (
<soi> <app0> <appn> <com> <dqt> <dht> <dri>
<frame_header> <scan_header> <entropy_data> <eoi>
)
# Fixed framing markers
<soi> ::= b'\xff\xd8'
<eoi> ::= b'\xff\xd9'
# APP0 / JFIF
# FFE0, length, "JFIF\0", version(2), density units, Xdensity, Ydensity,
# thumbnail width/height (0 = no embedded thumbnail).
<app0> ::= b'\xff\xe0' <app0_len> <app0_body>
<app0_len> ::= <byte> <byte>
<app0_body> ::= (
b'JFIF\x00' <byte> <byte> <dens_units>
<density> <density> b'\x00' b'\x00'
)
where bytes(<app0_len>) == (len(bytes(<app0_body>)) + 2).to_bytes(2, 'big')
<dens_units> ::= rb'[\x00-\x02]'
<density> ::= rb'[\x00\x01]' <byte>
# APPn (application-specific, APP1..APP15)
<appn> ::= <appn_marker> <appn_len> <appn_body>
<appn_marker> ::= rb'\xff[\xe1-\xef]'
<appn_len> ::= <byte> <byte>
<appn_body> ::= <byte>{0,32}
where bytes(<appn_len>) == (len(bytes(<appn_body>)) + 2).to_bytes(2, 'big')
# COM (comment)
<com> ::= b'\xff\xfe' <com_len> <com_body>
<com_len> ::= <byte> <byte>
<com_body> ::= rb'[\x20-\x7e]'{0,48}
where bytes(<com_len>) == (len(bytes(<com_body>)) + 2).to_bytes(2, 'big')
# DQT (define quantization table)
# One or more tables; each: Pq/Tq byte (precision 0 = 8-bit here, id 0..3)
# followed by 64 quantization coefficients.
<dqt> ::= b'\xff\xdb' <dqt_len> <dqt_body>
<dqt_len> ::= <byte> <byte>
<dqt_body> ::= <qtable>{1,4}
where bytes(<dqt_len>) == (len(bytes(<dqt_body>)) + 2).to_bytes(2, 'big')
<qtable> ::= <pq_tq> <byte>{64}
<pq_tq> ::= rb'[\x00-\x03]'
<dht> ::= b'\xff\xc4' <dht_len> <dht_body>
<dht_len> ::= <byte> <byte>
<dht_body> ::= <htable>{1,4}
where bytes(<dht_len>) == (len(bytes(<dht_body>)) + 2).to_bytes(2, 'big')
<htable> ::= <tc_th> <ht_counts> <ht_syms>
<tc_th> ::= rb'[\x00-\x03\x10-\x13]'
<ht_counts> ::= <ht_cnt>{16}
<ht_cnt> ::= rb'[\x00-\x03]'
<ht_syms> ::= <byte>{sum(bytes(<ht_counts>))}
# DRI (define restart interval)
<dri> ::= b'\xff\xdd' <dri_len> <dri_body>
<dri_len> ::= <byte> <byte>
<dri_body> ::= <byte> <byte>
where bytes(<dri_len>) == (len(bytes(<dri_body>)) + 2).to_bytes(2, 'big')
# SOFn (frame header)
# SOF0 baseline / SOF1 extended-sequential / SOF2 progressive.
# length = 8 + 3*Nf. Precision 8. Non-zero, modest dimensions.
# Explicit 1-component (grayscale) and 3-component (YCbCr) branches carry the
# component count byte and matching length literally.
<frame_header> ::= <sof_marker> <sof_body>
<sof_marker> ::= rb'\xff[\xc0-\xc2]'
<sof_body> ::= <sof_gray> | <sof_ycc>
<sof_gray> ::= (
b'\x00\x0b' b'\x08' <dim> <dim> b'\x01' <frame_comp>
)
<sof_ycc> ::= (
b'\x00\x11' b'\x08' <dim> <dim> b'\x03'
<frame_comp> <frame_comp> <frame_comp>
)
<dim> ::= rb'[\x00-\x07]' rb'[\x01-\xff]'
<frame_comp> ::= <byte> <sampling> <qt_sel>
<sampling> ::= rb'[\x11-\x14\x21-\x24\x31-\x34\x41-\x44]'
<qt_sel> ::= rb'[\x00-\x03]'
# SOS (scan header)
# length = 6 + 2*Ns; component selector + DC/AC Huffman table selector per
# component; spectral selection start/end + successive approximation.
<scan_header> ::= b'\xff\xda' <sos_body>
<sos_body> ::= <sos_gray> | <sos_ycc>
<sos_gray> ::= (
b'\x00\x08' b'\x01' <scan_comp> <spectral>
)
<sos_ycc> ::= (
b'\x00\x0c' b'\x03' <scan_comp> <scan_comp> <scan_comp> <spectral>
)
<scan_comp> ::= <byte> <td_ta>
<td_ta> ::= rb'[\x00-\x03\x10-\x13\x20-\x23\x30-\x33]'
<spectral> ::= b'\x00' b'\x3f' b'\x00'
# Entropy-coded data
# Byte-stuffed scan data: any non-FF byte, a stuffed FF (FF 00), or an RSTn
# restart marker (FF D0..FF D7). At least one restart chunk guarantees RSTn
# coverage.
<entropy_data> ::= <ecs> <restart_chunk>{1,3}
<ecs> ::= <entropy_byte>{0,96}
<restart_chunk> ::= <rst> <entropy_byte>{0,32}
<entropy_byte> ::= rb'[\x00-\xfe]' | b'\xff\x00' | rb'\xff[\xd0-\xd7]'
<rst> ::= rb'\xff[\xd0-\xd7]'
<byte> ::= rb'[\x00-\xff]'