Fanbase

heic/heic

version 1.0

A single HEVC-coded still image in the real ISOBMFF/HEIF box layout (ftyp, meta/hdlr/pitm/iloc/iinf/infe, iprp/ipco/ispe/hvcC/pixi/colr, idat); the HEVC NAL payloads are container-correct shells, not a decodable bitstream

Format
High Efficiency Image File Format (heic)
Authors
The Fandango Fuzzer Team
License
Apache-2.0
Fandango
>=1.3
Extensions
heic
Media type
image/heic
Specification
https://www.iso.org/standard/89035.html
Its files should decode
never
In the registry
specs/heic/heic/heic.fan

Use it

fanbase install heic
fandango fuzz -F heic -n 10

How it does

accepts 0%, covers 7.8%, 12/s. 1000 inputs, seed 1.

ParserAcceptsVersion
ffmpeg0%ffmpeg version 7.1.5-0+deb13u1
imagemagick0%Version: ImageMagick 7.1.1-43 Q16 x86_64 22550
libheif-cov0%libheif 1.23.6
pillow0%12.3.0

The spec says its files decode never: as expected.

libheif-cov

lines reached
7.8% of 22119
real files reach
18.9%
only the generated files
96 lines
only the real files
2550 lines
7.2% after 1 inputs, 7.8% after 1000real files: 18.9%

Cite it

@software{fanbase_heic_heic_1_0,
  author  = {{The Fandango Fuzzer Team}},
  title   = {High Efficiency Image File Format (heic/heic), a Fandango input specification},
  version = {1.0},
  url     = {https://github.com/fandango-fuzzer/fanbase},
  note    = {Fanbase registry, specs/heic/heic/heic.fan, sha256 479af94c4745},
}

Source

Source (229 lines)
# HEIC (ISO/IEC 23008-12): an ISOBMFF/HEIF box container -- ftyp, meta (hdlr,
# pitm, iloc, iinf/infe, iprp/ipco/ipma with ispe/hvcC/pixi/colr, idat)
# holding one HEVC-coded image item. Box sizes and the iloc/idat extent are
# tied to the generated bytes by `where` equalities.
#
# TODO:
# * The HEVC NAL payloads in idat and the VPS/SPS/PPS arrays are
#   container-correct shells, not a decodable bitstream, so files rarely
#   decode past the container level.

<start> ::= <heic_file>

<heic_file> ::= <ftyp_box> <meta_box>

# ftyp - File Type Box
<ftyp_box>     ::= <ftyp_size> <ftyp_content>
<ftyp_size>    ::= <u32>
<ftyp_content> ::= rb'ftyp' <major_brand> <minor_version> <compat_brands>
<major_brand>  ::= rb'heic'
<minor_version>::= rb'\x00\x00\x00\x00'
<compat_brands>::= <brand>{2,5}
<brand>        ::= rb'heic' | rb'heix' | rb'mif1' | rb'miaf' | rb'msf1' | rb'hevc'

# meta - Metadata Box (FullBox, version 0)
<meta_box>     ::= <meta_size> <meta_content>
<meta_size>    ::= <u32>
<meta_content> ::= rb'meta' <fullbox0> <hdlr_box> <pitm_box> <iloc_box> \
                   <iinf_box> <iprp_box> <idat_box>

# hdlr - Handler Reference Box (FullBox); handler_type must be 'pict'
<hdlr_box>     ::= <hdlr_size> <hdlr_content>
<hdlr_size>    ::= <u32>
<hdlr_content> ::= rb'hdlr' <fullbox0> <predefined> rb'pict' \
                   <hdlr_reserved> <handler_name>
<predefined>   ::= rb'\x00\x00\x00\x00'
<hdlr_reserved>::= rb'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
<handler_name> ::= <name_chars> rb'\x00'
<name_chars>   ::= <name_char>{0,16}
<name_char>    ::= rb'[\x20-\x7e]'

# pitm - Primary Item Box (FullBox); item_ID = 1
<pitm_box>     ::= <pitm_size> <pitm_content>
<pitm_size>    ::= <u32>
<pitm_content> ::= rb'pitm' <fullbox0> <primary_item_id>
<primary_item_id> ::= rb'\x00\x01'

# iloc - Item Location Box (FullBox, version 1).
# offset_size=4, length_size=4, base_offset_size=0, index_size=0.
# One item (ID 1) with construction_method=1 (idat), one extent whose
# extent_offset=0 and extent_length == number of coded-image bytes in idat.
<iloc_box>     ::= <iloc_size> <iloc_content>
<iloc_size>    ::= <u32>
<iloc_content> ::= rb'iloc' <iloc_ver> <iloc_flags> <iloc_sizes> <iloc_base_index> \
                   <iloc_item_count> <iloc_item_id> <iloc_constr_method> \
                   <iloc_data_ref_index> <iloc_extent_count> \
                   <iloc_extent_offset> <iloc_extent_length>
<iloc_ver>          ::= rb'\x01'
<iloc_flags>        ::= rb'\x00\x00\x00'
<iloc_sizes>        ::= rb'\x44'
<iloc_base_index>   ::= rb'\x00'
<iloc_item_count>   ::= rb'\x00\x01'
<iloc_item_id>      ::= rb'\x00\x01'
<iloc_constr_method>::= rb'\x00\x01'
<iloc_data_ref_index>::= rb'\x00\x00'
<iloc_extent_count> ::= rb'\x00\x01'
<iloc_extent_offset>::= rb'\x00\x00\x00\x00'
<iloc_extent_length>::= <u32>

# iinf - Item Info Box (FullBox) with one infe entry
<iinf_box>     ::= <iinf_size> <iinf_content>
<iinf_size>    ::= <u32>
<iinf_content> ::= rb'iinf' <fullbox0> <iinf_entry_count> <infe_box>
<iinf_entry_count> ::= rb'\x00\x01'

# infe - Item Info Entry (FullBox, version 2); item_type = 'hvc1'
<infe_box>     ::= <infe_size> <infe_content>
<infe_size>    ::= <u32>
<infe_content> ::= rb'infe' <infe_ver> <infe_flags> <infe_item_id> \
                   <infe_prot_index> rb'hvc1' <infe_item_name>
<infe_ver>       ::= rb'\x02'
<infe_flags>     ::= rb'\x00\x00\x00'
<infe_item_id>   ::= rb'\x00\x01'
<infe_prot_index>::= rb'\x00\x00'
<infe_item_name> ::= <name_chars> rb'\x00'

# iprp - Item Properties Box  ->  ipco (container) + ipma (association)
<iprp_box>     ::= <iprp_size> <iprp_content>
<iprp_size>    ::= <u32>
<iprp_content> ::= rb'iprp' <ipco_box> <ipma_box>

# ipco - Item Property Container Box  (ordered list of property boxes)
<ipco_box>     ::= <ipco_size> <ipco_content>
<ipco_size>    ::= <u32>
<ipco_content> ::= rb'ipco' <ispe_box> <hvcC_box> <pixi_box> <colr_box>

# ispe - Image Spatial Extents (FullBox); width x height
<ispe_box>     ::= <ispe_size> <ispe_content>
<ispe_size>    ::= <u32>
<ispe_content> ::= rb'ispe' <fullbox0> <ispe_width> <ispe_height>
<ispe_width>   ::= <dimension>
<ispe_height>  ::= <dimension>
<dimension>    ::= rb'\x00\x00\x00\x40' | rb'\x00\x00\x00\x80' \
                 | rb'\x00\x00\x01\x00' | rb'\x00\x00\x02\x00' \
                 | rb'\x00\x00\x04\x00' | rb'\x00\x00\x07\x80'

# hvcC - HEVC Configuration Box (HEVCDecoderConfigurationRecord).
#   configurationVersion=1
#   general_profile_space(2)+general_tier_flag(1)+general_profile_idc(5)
#   general_profile_compatibility_flags (u32)
#   general_constraint_indicator_flags (48 bits = 6 bytes)
#   general_level_idc (u8)
#   '1111' + min_spatial_segmentation_idc(12)
#   '111111' + parallelismType(2)
#   '111111' + chromaFormat(2)
#   '11111' + bitDepthLumaMinus8(3)
#   '11111' + bitDepthChromaMinus8(3)
#   avgFrameRate (u16)
#   constantFrameRate(2)+numTemporalLayers(3)+temporalIdNested(1)+lengthSizeMinusOne(2)
#   numOfArrays (u8) = 3 (VPS, SPS, PPS)
<hvcC_box>     ::= <hvcC_size> <hvcC_content>
<hvcC_size>    ::= <u32>
<hvcC_content> ::= rb'hvcC' <hvcc_config_ver> <hvcc_profile> <hvcc_compat_flags> \
                   <hvcc_constraint_flags> <hvcc_level_idc> <hvcc_min_seg> \
                   <hvcc_parallelism> <hvcc_chroma_format> <hvcc_bitdepth_luma> \
                   <hvcc_bitdepth_chroma> <hvcc_avg_framerate> <hvcc_frame_info> \
                   <hvcc_num_arrays> <nal_array_vps> <nal_array_sps> <nal_array_pps>
<hvcc_config_ver>      ::= rb'\x01'
<hvcc_profile>         ::= rb'\x01' | rb'\x02' | rb'\x03' | rb'\x04'
<hvcc_compat_flags>    ::= rb'\x60\x00\x00\x00' | rb'\x40\x00\x00\x00' | rb'\x20\x00\x00\x00'
<hvcc_constraint_flags>::= rb'\x00\x00\x00\x00\x00\x00' | rb'\x90\x00\x00\x00\x00\x00'
<hvcc_level_idc>       ::= rb'\x5d' | rb'\x78' | rb'\x7b' | rb'\x99' | rb'\x9c'
<hvcc_min_seg>         ::= rb'\xf0\x00'
<hvcc_parallelism>     ::= rb'\xfc'
<hvcc_chroma_format>   ::= rb'\xfd' | rb'\xfe' | rb'\xff'
<hvcc_bitdepth_luma>   ::= rb'\xf8' | rb'\xf9' | rb'\xfa' | rb'\xfc'
<hvcc_bitdepth_chroma> ::= rb'\xf8' | rb'\xf9' | rb'\xfa' | rb'\xfc'
<hvcc_avg_framerate>   ::= rb'\x00\x00'
<hvcc_frame_info>      ::= rb'\x0f'
<hvcc_num_arrays>      ::= rb'\x03'

# Each NAL array: header byte (array_completeness(1)+reserved(1)+NAL_unit_type(6)),
# numNalus:u16=1, then one NAL unit: nalUnitLength:u16 + nalUnit bytes.
# VPS=type 32 (0x20), SPS=type 33 (0x21), PPS=type 34 (0x22).
<nal_array_vps> ::= <nal_hdr_vps> <nal_count> <vps_len> <vps_data>
<nal_array_sps> ::= <nal_hdr_sps> <nal_count> <sps_len> <sps_data>
<nal_array_pps> ::= <nal_hdr_pps> <nal_count> <pps_len> <pps_data>
<nal_hdr_vps>  ::= rb'\x20' | rb'\xa0'
<nal_hdr_sps>  ::= rb'\x21' | rb'\xa1'
<nal_hdr_pps>  ::= rb'\x22' | rb'\xa2'
<nal_count>    ::= rb'\x00\x01'
<vps_len>      ::= <u16>
<sps_len>      ::= <u16>
<pps_len>      ::= <u16>
<vps_data>     ::= <byte>{6,40}
<sps_data>     ::= <byte>{6,40}
<pps_data>     ::= <byte>{4,32}

# pixi - Pixel Information (FullBox); 3 channels
<pixi_box>     ::= <pixi_size> <pixi_content>
<pixi_size>    ::= <u32>
<pixi_content> ::= rb'pixi' <fullbox0> <pixi_num_channels> <pixi_bits>
<pixi_num_channels> ::= rb'\x03'
<pixi_bits>    ::= <pixi_depth> <pixi_depth> <pixi_depth>
<pixi_depth>   ::= rb'\x08' | rb'\x0a' | rb'\x0c'

# colr - Colour Information Box, nclx profile
<colr_box>     ::= <colr_size> <colr_content>
<colr_size>    ::= <u32>
<colr_content> ::= rb'colr' rb'nclx' <colour_primaries> <transfer_char> \
                   <matrix_coeff> <full_range_byte>
<colour_primaries> ::= rb'\x00\x01' | rb'\x00\x02' | rb'\x00\x09' | rb'\x00\x0c'
<transfer_char>    ::= rb'\x00\x01' | rb'\x00\x02' | rb'\x00\x0d' | rb'\x00\x10'
<matrix_coeff>     ::= rb'\x00\x00' | rb'\x00\x01' | rb'\x00\x02' | rb'\x00\x09'
<full_range_byte>  ::= rb'\x80' | rb'\x00'

# ipma - Item Property Association (FullBox); associates ipco entries with item 1
#   entry_count:u32=1, item_ID:u16=1, association_count:u8=4,
#   4x association bytes: essential(1) | property_index(7)  (1-based ipco order)
<ipma_box>     ::= <ipma_size> <ipma_content>
<ipma_size>    ::= <u32>
<ipma_content> ::= rb'ipma' <fullbox0> <ipma_entry_count> <ipma_item_id> \
                   <ipma_assoc_count> <ipma_assoc_ispe> <ipma_assoc_hvcc> \
                   <ipma_assoc_pixi> <ipma_assoc_colr>
<ipma_entry_count> ::= rb'\x00\x00\x00\x01'
<ipma_item_id>     ::= rb'\x00\x01'
<ipma_assoc_count> ::= rb'\x04'
<ipma_assoc_ispe>  ::= rb'\x01'
<ipma_assoc_hvcc>  ::= rb'\x82'
<ipma_assoc_pixi>  ::= rb'\x03'
<ipma_assoc_colr>  ::= rb'\x04'

# idat - Item Data Box; the coded HEVC image bytes referenced by iloc.
# Shaped as a length-prefixed HEVC coded-slice NAL-unit shell.
<idat_box>     ::= <idat_size> <idat_content>
<idat_size>    ::= <u32>
<idat_content> ::= rb'idat' <hevc_image_data>
<hevc_image_data> ::= <hevc_nal_header> <hevc_nal_blob>
<hevc_nal_header> ::= rb'\x26\x01' | rb'\x28\x01' | rb'\x02\x01'
<hevc_nal_blob>   ::= <byte>{16,512}

# Shared terminals
<fullbox0> ::= rb'\x00\x00\x00\x00'
<u32>      ::= <byte> <byte> <byte> <byte>
<u16>      ::= <byte> <byte>
<byte>     ::= rb'[\x00-\xff]'

# Constraints: every box's size field == 4 (its own bytes) + the length of the
# rest of the box, tied to the actually-generated content; the iloc extent
# length == the number of coded-image bytes stored in idat; and each hvcC NAL
# array's nalUnitLength == the byte length of that NAL unit.
where bytes(<ftyp_size>) == (4 + len(bytes(<ftyp_content>))).to_bytes(4, 'big')
where bytes(<meta_size>) == (4 + len(bytes(<meta_content>))).to_bytes(4, 'big')
where bytes(<hdlr_size>) == (4 + len(bytes(<hdlr_content>))).to_bytes(4, 'big')
where bytes(<pitm_size>) == (4 + len(bytes(<pitm_content>))).to_bytes(4, 'big')
where bytes(<iloc_size>) == (4 + len(bytes(<iloc_content>))).to_bytes(4, 'big')
where bytes(<iinf_size>) == (4 + len(bytes(<iinf_content>))).to_bytes(4, 'big')
where bytes(<infe_size>) == (4 + len(bytes(<infe_content>))).to_bytes(4, 'big')
where bytes(<iprp_size>) == (4 + len(bytes(<iprp_content>))).to_bytes(4, 'big')
where bytes(<ipco_size>) == (4 + len(bytes(<ipco_content>))).to_bytes(4, 'big')
where bytes(<ispe_size>) == (4 + len(bytes(<ispe_content>))).to_bytes(4, 'big')
where bytes(<hvcC_size>) == (4 + len(bytes(<hvcC_content>))).to_bytes(4, 'big')
where bytes(<pixi_size>) == (4 + len(bytes(<pixi_content>))).to_bytes(4, 'big')
where bytes(<colr_size>) == (4 + len(bytes(<colr_content>))).to_bytes(4, 'big')
where bytes(<ipma_size>) == (4 + len(bytes(<ipma_content>))).to_bytes(4, 'big')
where bytes(<idat_size>) == (4 + len(bytes(<idat_content>))).to_bytes(4, 'big')
where bytes(<iloc_extent_length>) == len(bytes(<hevc_image_data>)).to_bytes(4, 'big')
where bytes(<vps_len>) == len(bytes(<vps_data>)).to_bytes(2, 'big')
where bytes(<sps_len>) == len(bytes(<sps_data>)).to_bytes(2, 'big')
where bytes(<pps_len>) == len(bytes(<pps_data>)).to_bytes(2, 'big')