ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
unarch_gzip.c File Reference

RFC 1952 gzip member decoder over the vendored miniz tinfl core. More...

#include "unarch_gzip.h"
#include <string.h>
#include "miniz.h"
#include "ra8_attributes.h"
#include "ra8_check.h"
Include dependency graph for unarch_gzip.c:

Go to the source code of this file.

Data Structures

struct  gz_src_t
 Cursor-tracked exact-read byte source with a running header CRC. More...
struct  gz_inflate_t
 Loop-carried state of the windowed DEFLATE inflation. More...

Enumerations

enum  gz_grammar_t : uint32_t {
  k_gz_id1 = 0x1FU ,
  k_gz_id2 = 0x8BU ,
  k_gz_cm_deflate = 8U ,
  k_gz_flg_fhcrc = 0x02U ,
  k_gz_flg_fextra = 0x04U ,
  k_gz_flg_fname = 0x08U ,
  k_gz_flg_fcomment = 0x10U ,
  k_gz_flg_reserved = 0xE0U ,
  k_gz_hdr_fixed = 10U ,
  k_gz_xlen_bytes = 2U ,
  k_gz_fhcrc_bytes = 2U ,
  k_gz_trailer_len = 8U ,
  k_gz_idx_id1 = 0U ,
  k_gz_idx_id2 = 1U ,
  k_gz_idx_cm = 2U ,
  k_gz_idx_flg = 3U ,
  k_gz_in_chunk = 512U ,
  k_gz_out_window = 4096U ,
  k_gz_shift_byte = 8U ,
  k_gz_crc16_mask = 0xFFFFU ,
  k_gz_isize_mask = 0xFFFFFFFFU
}
 RFC 1952 constants and this decoder's loop windows. More...

Functions

static ra8_err_t internal_src_take (gz_src_t *s, uint8_t *dst, size_t n)
 Consume exactly n bytes from the source into dst.
static ra8_err_t internal_skip_string (gz_src_t *s)
 Skip a NUL-terminated header string (FNAME / FCOMMENT), bounded.
static ra8_err_t internal_skip_fextra (gz_src_t *s)
 Skip the FEXTRA subfield block (RFC 1952 XLEN + XLEN bytes), bounded.
static ra8_err_t internal_parse_header (gz_src_t *s)
 Parse the whole RFC 1952 header, leaving the cursor at the data.
static ra8_err_t internal_inflate_pass (gz_inflate_t *st)
 Run one refill + inflate pass of the DEFLATE loop.
static ra8_err_t internal_verify_trailer (const gz_inflate_t *st)
 Verify the gzip trailer against the produced payload.
bool unarch_gzip_magic (const uint8_t *sig, size_t sig_len)
 Whether sig begins with the gzip member magic (1F 8B).
ra8_err_t unarch_gzip_unwrap (unarch_read_fn read, void *ctx, uint64_t size, uint8_t *out, size_t out_cap, const ra8_decomp_limits_t *limits, size_t *out_len)
 Decode one whole gzip member from a read seam into a caller arena.

Variables

static tinfl_decompressor s_gz_inflator
 The single-client tinfl state (too large for a task stack).

Detailed Description

RFC 1952 gzip member decoder over the vendored miniz tinfl core.

Tag
[Ring 4 / Domain] {World: NS}

See unarch_gzip.h for the contract. The TU is organised as a small cursor-tracked byte source over the shared read seam (exact reads, running CRC32 for the optional FHCRC header check), a fail-closed header parser, a windowed tinfl_decompress loop that charges the unified decompression budget every pass, and a trailer verifier. The DEFLATE decompressor state (~11 KiB) lives in one module-static arena rather than the stack (single-client, zero heap – the same idiom as the XZ pool), so the reader task's stack budget is untouched.

Since
Version 0.1.0

Definition in file unarch_gzip.c.

Enumeration Type Documentation

◆ gz_grammar_t

enum gz_grammar_t : uint32_t

RFC 1952 constants and this decoder's loop windows.

Since
Version 0.1.0
Enumerator
k_gz_id1 

Magic byte 0.

k_gz_id2 

Magic byte 1.

k_gz_cm_deflate 

The only defined compression method.

k_gz_flg_fhcrc 

Header CRC16 present.

k_gz_flg_fextra 

Extra field present.

k_gz_flg_fname 

Original file name present.

k_gz_flg_fcomment 

Comment present.

k_gz_flg_reserved 

Reserved bits: must be zero.

k_gz_hdr_fixed 

Fixed header length in bytes.

k_gz_xlen_bytes 

FEXTRA length-field size.

k_gz_fhcrc_bytes 

FHCRC field size.

k_gz_trailer_len 

CRC32 + ISIZE trailer length.

k_gz_idx_id1 

Header index of ID1.

k_gz_idx_id2 

Header index of ID2.

k_gz_idx_cm 

Header index of CM.

k_gz_idx_flg 

Header index of FLG.

k_gz_in_chunk 

Input refill window per pass.

k_gz_out_window 

Output window per pass (charge granularity for bomb detection).

k_gz_shift_byte 

Bits per byte for LE assembly.

k_gz_crc16_mask 

FHCRC is the low 16 CRC32 bits.

k_gz_isize_mask 

ISIZE is the payload length mod 2^32.

Definition at line 42 of file unarch_gzip.c.

Function Documentation

◆ internal_inflate_pass()

ra8_err_t internal_inflate_pass ( gz_inflate_t * st)
static

Run one refill + inflate pass of the DEFLATE loop.

Charges one iteration, refills the input window when drained, inflates into a bounded output window, folds the produced bytes into the payload CRC, and charges the output budget. Truncation (input exhausted before stream end), corruption, and an overrun arena each fail closed.

Parameters
[in,out]stInflation state.
Returns
ra8_err_t status of this pass.
Return values
k_ra8_okPass complete; check st->done.
k_ra8_err_no_memArena full with the stream open.
k_ra8_err_validation_failedTruncated / corrupt stream.
k_ra8_err_decomp_*A policy bound was breached.
Precondition
st->src is positioned inside the DEFLATE stream.
st->done is false.
Postcondition
On k_ra8_ok the cursors advanced by the consumed/produced counts.
On any error the member is rejected.
Note
Not thread-safe (module-static tinfl state).
Since
Version 0.1.0

Definition at line 314 of file unarch_gzip.c.

References gz_inflate_t::budget, gz_inflate_t::crc, gz_inflate_t::done, internal_src_take(), k_gz_out_window, k_ra8_err_no_mem, k_ra8_err_validation_failed, k_ra8_ok, gz_inflate_t::out, gz_inflate_t::out_cap, gz_src_t::pos, ra8_decomp_budget_charge_iter(), ra8_decomp_budget_charge_output(), s_gz_inflator, gz_src_t::size, gz_inflate_t::src, gz_inflate_t::total, gz_inflate_t::win, gz_inflate_t::win_len, and gz_inflate_t::win_ofs.

Referenced by unarch_gzip_unwrap().

◆ internal_parse_header()

ra8_err_t internal_parse_header ( gz_src_t * s)
static

Parse the whole RFC 1952 header, leaving the cursor at the data.

Fixed fields first (magic, method, flags – reserved bits rejected), then the optional fields in wire order: FEXTRA (bounded by its own length field), FNAME, FCOMMENT (bounded strings), and FHCRC (verified against the running CRC of every header byte before it).

Parameters
[in,out]sByte source at offset 0.
Returns
ra8_err_t status.
Return values
k_ra8_okHeader consumed; cursor at DEFLATE.
k_ra8_err_not_supportedBad magic / method / reserved bits.
k_ra8_err_checksum_mismatchFHCRC verification failed.
k_ra8_err_validation_failedTruncated or over-long fields.
Precondition
s was bound with pos == 0 and CRC tracking on.
The member is at least the fixed header long (checked here).
Postcondition
On k_ra8_ok, s->pos is the DEFLATE stream offset.
On any error the member is rejected.
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 215 of file unarch_gzip.c.

References gz_src_t::crc, internal_skip_fextra(), internal_skip_string(), internal_src_take(), k_gz_cm_deflate, k_gz_crc16_mask, k_gz_fhcrc_bytes, k_gz_flg_fcomment, k_gz_flg_fextra, k_gz_flg_fhcrc, k_gz_flg_fname, k_gz_flg_reserved, k_gz_hdr_fixed, k_gz_id1, k_gz_id2, k_gz_idx_cm, k_gz_idx_flg, k_gz_idx_id1, k_gz_idx_id2, k_gz_shift_byte, k_ra8_err_checksum_mismatch, k_ra8_err_not_supported, k_ra8_ok, and gz_src_t::track_crc.

Referenced by unarch_gzip_unwrap().

◆ internal_skip_fextra()

ra8_err_t internal_skip_fextra ( gz_src_t * s)
static

Skip the FEXTRA subfield block (RFC 1952 XLEN + XLEN bytes), bounded.

Reads the 2-byte little-endian XLEN, then consumes exactly that many bytes in k_gz_in_chunk-sized pieces. XLEN is at most 65535, so the loop is statically bounded.

Parameters
[in,out]sByte source positioned at the FEXTRA XLEN field.
Returns
ra8_err_t status.
Return values
k_ra8_okXLEN and its payload consumed.
k_ra8_err_validation_failedXLEN or the subfield ran past the member.
Precondition
s was bound to a live reader.
The FEXTRA header flag was set.
Postcondition
On k_ra8_ok the cursor sits just past the subfield block.
On any error the parse must stop (fail-closed).
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 173 of file unarch_gzip.c.

References internal_src_take(), k_gz_in_chunk, k_gz_shift_byte, k_gz_xlen_bytes, and k_ra8_ok.

Referenced by internal_parse_header().

◆ internal_skip_string()

ra8_err_t internal_skip_string ( gz_src_t * s)
static

Skip a NUL-terminated header string (FNAME / FCOMMENT), bounded.

Consumes bytes until the terminator; a string longer than k_unarch_gzip_str_max or an unterminated one (EOF first) rejects the member.

Parameters
[in,out]sByte source positioned at the string start.
Returns
ra8_err_t status.
Return values
k_ra8_okTerminator consumed.
k_ra8_err_validation_failedOver-long or unterminated string.
Precondition
s was bound to a live reader.
The header flag for this string was set.
Postcondition
On k_ra8_ok the cursor sits just past the NUL.
On any error the parse must stop (fail-closed).
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 141 of file unarch_gzip.c.

References internal_src_take(), k_ra8_err_validation_failed, k_ra8_ok, and k_unarch_gzip_str_max.

Referenced by internal_parse_header().

◆ internal_src_take()

ra8_err_t internal_src_take ( gz_src_t * s,
uint8_t * dst,
size_t n )
static

Consume exactly n bytes from the source into dst.

A short read (EOF under the parser) fails closed; consumed bytes advance the cursor and, while tracking, the running CRC.

Parameters
[in,out]sByte source.
[out]dstDestination (n writable bytes).
[in]nBytes required.
Returns
ra8_err_t status.
Return values
k_ra8_okBytes consumed.
k_ra8_err_validation_failedTruncated member.
Precondition
s was bound to a live reader.
dst holds n writable bytes.
Postcondition
On k_ra8_ok the cursor advanced by exactly n.
On any error the parse must stop (fail-closed).
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 108 of file unarch_gzip.c.

References gz_src_t::crc, gz_src_t::ctx, k_ra8_err_validation_failed, k_ra8_ok, gz_src_t::pos, gz_src_t::read, gz_src_t::size, and gz_src_t::track_crc.

Referenced by internal_inflate_pass(), internal_parse_header(), internal_skip_fextra(), and internal_skip_string().

◆ internal_verify_trailer()

ra8_err_t internal_verify_trailer ( const gz_inflate_t * st)
static

Verify the gzip trailer against the produced payload.

The trailer starts at the first unconsumed input byte: win_len - win_ofs leftover window bytes plus whatever the source still holds. CRC32 and ISIZE must both match, and nothing may follow the trailer.

Parameters
[in]stThe completed inflation state.
Returns
ra8_err_t status.
Return values
k_ra8_okTrailer verified, member ends cleanly.
k_ra8_err_checksum_mismatchCRC32 or ISIZE mismatch.
k_ra8_err_validation_failedTruncated trailer or trailing bytes.
Precondition
st->done is true (tinfl consumed the whole DEFLATE stream).
The source cursor sits at the window's end.
Postcondition
No state is modified (pure verification).
On any error the member is rejected.
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 391 of file unarch_gzip.c.

References gz_inflate_t::crc, gz_src_t::ctx, k_gz_isize_mask, k_gz_shift_byte, k_gz_trailer_len, k_ra8_err_checksum_mismatch, k_ra8_err_validation_failed, k_ra8_ok, gz_src_t::pos, gz_src_t::read, gz_src_t::size, gz_inflate_t::src, gz_inflate_t::total, gz_inflate_t::win_len, and gz_inflate_t::win_ofs.

Referenced by unarch_gzip_unwrap().

◆ unarch_gzip_magic()

bool unarch_gzip_magic ( const uint8_t * sig,
size_t sig_len )
nodiscard

Whether sig begins with the gzip member magic (1F 8B).

Pure signature probe so open paths can route a wrapped archive without constructing a decoder.

Parameters
[in]sigLeading archive bytes (may be NULL).
[in]sig_lenReadable length of sig in bytes.
Returns
Whether the bytes begin a gzip member.
Return values
trueBoth magic bytes match.
falsesig is NULL, too short, or not gzip.
Precondition
sig holds sig_len readable bytes when non-NULL.
sig_len is the true readable length (untrusted values are safe).
Postcondition
No state is modified (pure read).
The result depends only on the first two bytes.
Note
Thread-safe: pure read.
See also
unarch_gzip_unwrap()
Since
Version 0.1.0

Definition at line 422 of file unarch_gzip.c.

References k_gz_id1, k_gz_id2, k_gz_idx_id1, k_gz_idx_id2, and k_unarch_gzip_sig_len.

Referenced by comic_open_wrapped(), and internal_open_unwrapped().

◆ unarch_gzip_unwrap()

ra8_err_t unarch_gzip_unwrap ( unarch_read_fn read,
void * ctx,
uint64_t size,
uint8_t * out,
size_t out_cap,
const ra8_decomp_limits_t * limits,
size_t * out_len )
nodiscard

Decode one whole gzip member from a read seam into a caller arena.

Parses the RFC 1952 header, inflates the DEFLATE stream in bounded passes charged against limits, and verifies the trailer (payload CRC32 + ISIZE). Fail-closed on everything else: a non-gzip or non-DEFLATE header, reserved flag bits, oversized name/comment fields, a failed header or payload checksum, truncation, corruption, output overrun, a policy breach, and trailing bytes after the member.

Parameters
[in]readByte reader over the gzip member (non-NULL).
[in]ctxContext passed to read.
[in]sizeMember length in bytes (> 0).
[out]outDestination arena (non-NULL, out_cap bytes).
[in]out_capCapacity of out in bytes (> 0).
[in]limitsPolicy to enforce, or NULL for the default.
[out]out_lenReceives the decoded byte count (non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okMember decoded and verified.
k_ra8_err_null_ptrA required pointer was NULL.
k_ra8_err_invalid_sizesize or out_cap is 0.
k_ra8_err_invalid_arglimits has a zero field.
k_ra8_err_not_supportedNot gzip, not DEFLATE, or reserved flag bits set.
k_ra8_err_no_memout is too small for the member.
k_ra8_err_checksum_mismatchHeader FHCRC, payload CRC32, or ISIZE verification failed.
k_ra8_err_validation_failedTruncated / corrupt stream, oversized header field, or trailing bytes after the member.
k_ra8_err_decomp_output_capOutput exceeds the policy cap.
k_ra8_err_decomp_ratioOutput breached the ratio bound.
k_ra8_err_decomp_iterationsThe decode loop budget ran out.
Precondition
read serves offsets [0, size) of the member.
No other gzip decode is in flight (module-static DEFLATE state).
Postcondition
On k_ra8_ok, out[0..*out_len) holds the decoded bytes.
On any error *out_len == 0.
Note
Not thread-safe (single-client DEFLATE state).
See also
unarch_gzip_magic()
Since
Version 0.1.0

Definition at line 437 of file unarch_gzip.c.

References gz_inflate_t::budget, gz_inflate_t::crc, gz_inflate_t::done, internal_inflate_pass(), internal_parse_header(), internal_verify_trailer(), k_ra8_err_invalid_size, k_ra8_err_null_ptr, k_ra8_ok, gz_inflate_t::out, gz_inflate_t::out_cap, RA8_CHECK_NULL_PTR, ra8_decomp_budget_init(), ra8_log_error, s_gz_inflator, gz_inflate_t::src, and gz_inflate_t::total.

Referenced by internal_unwrap().

Variable Documentation

◆ s_gz_inflator

tinfl_decompressor s_gz_inflator
static

The single-client tinfl state (too large for a task stack).

Definition at line 88 of file unarch_gzip.c.

Referenced by internal_inflate_pass(), and unarch_gzip_unwrap().