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

Bounded XZ/LZMA2 decode wrapper over the vendored xz-embedded. More...

#include "unarch_xz.h"
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "unarch_xz_pool.h"
#include "xz_config.h"
Include dependency graph for unarch_xz.c:

Go to the source code of this file.

Data Structures

struct  xz_unwrap_state_t
 Loop-carried state of one unwrap: cursors, session, and budget. More...

Enumerations

enum  xz_wrap_dims_t : uint16_t {
  k_xz_chunk = 512U ,
  k_xz_out_window = 4096U
}
 Fixed sizes for the unwrap input loop. More...
enum  xz_magic_t : uint8_t {
  k_xz_magic_b0 = 0xFDU ,
  k_xz_magic_b1 = 0x37U ,
  k_xz_magic_b2 = 0x7AU ,
  k_xz_magic_b3 = 0x58U ,
  k_xz_magic_b4 = 0x5AU ,
  k_xz_magic_b5 = 0x00U
}
 The six XZ stream header magic bytes (FD 37 7A 58 5A 00). More...

Functions

bool unarch_xz_magic (const uint8_t *sig, size_t sig_len)
 Whether sig begins with the XZ stream header magic.
ra8_err_t unarch_xz_stream_begin (unarch_xz_stream_t *xs, void *scratch, uint32_t scratch_len)
 Begin a multi-call XZ decode session over a caller scratch.
static ra8_err_t internal_xz_map_err (enum xz_ret ret)
 Map an xz-embedded return code onto the wrapper's error space.
ra8_err_t unarch_xz_stream_run (unarch_xz_stream_t *xs, const uint8_t *in, size_t in_len, size_t *in_used, uint8_t *out, size_t out_cap, size_t *out_used, bool *end)
 Feed one input chunk through a live session, producing output.
void unarch_xz_stream_end (unarch_xz_stream_t *xs)
 End a session: free the decoder and release the allocation pool.
static ra8_err_t internal_unwrap_reject_null (unarch_read_fn read, const uint8_t *out, const void *scratch, const size_t *out_len)
 Reject any NULL required argument to unarch_xz_unwrap.
static ra8_err_t internal_unwrap_pass (xz_unwrap_state_t *st, bool *end)
 Run one refill + decode pass of the unwrap loop.
ra8_err_t unarch_xz_unwrap (unarch_read_fn read, void *ctx, uint64_t size, uint8_t *out, size_t out_cap, void *scratch, uint32_t scratch_len, const ra8_decomp_limits_t *limits, size_t *out_len)
 Decode one whole XZ stream from a read seam into a caller arena.

Variables

static const char *const s_tag_xz = "unarch_xz"
 Log tag for XZ-wrapper diagnostics.

Detailed Description

Bounded XZ/LZMA2 decode wrapper over the vendored xz-embedded.

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

See unarch_xz.h for the contract. The session shape drives the SOUP decoder in XZ_PREALLOC mode: begin installs the caller scratch as the zero-heap allocation arena (unarch_xz_pool.h), reserves k_unarch_xz_state_reserve of it for the decoder state, and hands the remainder to xz-embedded as the maximum LZMA2 dictionary – everything is allocated once at init and a stream declaring a bigger dictionary fails with XZ_MEMLIMIT_ERROR (mapped to k_ra8_err_no_mem) instead of growing. The unwrap shape loops a session over the shared read seam in fixed k_xz_chunk input windows, charging the unified decompression budget every pass so a bomb or stuck stream terminates within policy.

Every decision in this file is a single condition on purpose: the wrapper fronts the most hostile input in the content path, so each bound is tested independently (and carries no compound-decision MC/DC burden).

Since
Version 0.1.0

Definition in file unarch_xz.c.

Enumeration Type Documentation

◆ xz_magic_t

enum xz_magic_t : uint8_t

The six XZ stream header magic bytes (FD 37 7A 58 5A 00).

Values from the .xz file format specification section 2.1.1.1; named per the no-magic-numbers rule.

Since
Version 0.1.0
Enumerator
k_xz_magic_b0 

Non-ASCII lead-in byte.

k_xz_magic_b1 

'7'.

k_xz_magic_b2 

'z'.

k_xz_magic_b3 

'X'.

k_xz_magic_b4 

'Z'.

k_xz_magic_b5 

NUL terminator of the magic.

Definition at line 68 of file unarch_xz.c.

◆ xz_wrap_dims_t

enum xz_wrap_dims_t : uint16_t

Fixed sizes for the unwrap input loop.

One k_xz_chunk stack window is refilled from the read seam per decode pass; small enough for the firmware stack budget, large enough that a policy-conformant stream finishes far inside the default iteration budget.

Since
Version 0.1.0
Enumerator
k_xz_chunk 

Input refill window per decode pass, bytes.

k_xz_out_window 

Output window per decode pass, bytes; keeps the budget charge granularity (and thus bomb detection) within one window of the bound.

Definition at line 54 of file unarch_xz.c.

Function Documentation

◆ internal_unwrap_pass()

ra8_err_t internal_unwrap_pass ( xz_unwrap_state_t * st,
bool * end )
static

Run one refill + decode pass of the unwrap loop.

Charges one iteration, refills the input window from the read seam at the current cursor, runs the session, advances both cursors, and charges the produced output against the budget. A zero-byte refill before the stream ended is a truncated backing (fail-closed); a full arena with the stream still open is an undersized arena.

Parameters
[in,out]stUnwrap state (live session, cursors in range).
[out]endReceives true when the stream verified and ended.
Returns
ra8_err_t status of this pass.
Return values
k_ra8_okPass complete; check end.
k_ra8_err_no_memArena full with the stream open, or the dictionary over budget.
k_ra8_err_validation_failedTruncated / corrupt / stuck stream.
k_ra8_err_not_supportedUnsupported format/check/filter.
k_ra8_err_decomp_*A policy bound was breached.
Precondition
st->xs is live and st->in_off <= st->size.
st->total_out <= st->out_cap.
Postcondition
On k_ra8_ok the cursors advanced by the consumed/produced counts.
On any error the caller must end the session (not recoverable).
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 307 of file unarch_xz.c.

References xz_unwrap_state_t::budget, xz_unwrap_state_t::chunk, xz_unwrap_state_t::ctx, xz_unwrap_state_t::in_off, k_ra8_err_no_mem, k_ra8_err_validation_failed, k_ra8_ok, k_xz_chunk, k_xz_out_window, xz_unwrap_state_t::out, xz_unwrap_state_t::out_cap, ra8_decomp_budget_charge_iter(), ra8_decomp_budget_charge_output(), xz_unwrap_state_t::read, xz_unwrap_state_t::size, xz_unwrap_state_t::total_out, unarch_xz_stream_run(), and xz_unwrap_state_t::xs.

Referenced by unarch_xz_unwrap().

◆ internal_unwrap_reject_null()

ra8_err_t internal_unwrap_reject_null ( unarch_read_fn read,
const uint8_t * out,
const void * scratch,
const size_t * out_len )
static

Reject any NULL required argument to unarch_xz_unwrap.

Runs the mandatory null guards so the entry point stays within the function-size budget; scalar validation stays inline there.

Parameters
[in]readByte reader over the stream.
[in]outDestination arena.
[in]scratchSession scratch.
[in]out_lenDecoded-length out-pointer.
Returns
ra8_err_t status.
Return values
k_ra8_okEvery required argument is non-NULL.
k_ra8_err_null_ptrSome required argument was NULL.
Precondition
The caller forwards unarch_xz_unwrap's arguments unchanged.
No argument is dereferenced before this returns k_ra8_ok.
Postcondition
On k_ra8_ok each checked pointer is safe to use.
On any error the reason is logged against s_tag_xz.
Note
Thread-safe: reads only its pointer arguments.
Since
Version 0.1.0

Definition at line 245 of file unarch_xz.c.

References k_ra8_err_null_ptr, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, and s_tag_xz.

Referenced by unarch_xz_unwrap().

◆ internal_xz_map_err()

ra8_err_t internal_xz_map_err ( enum xz_ret ret)
static

Map an xz-embedded return code onto the wrapper's error space.

The success codes (XZ_OK, XZ_STREAM_END) are handled by the caller before this runs; every remaining code is a bounded failure. XZ_MEMLIMIT_ERROR is the declared-dictionary breach; XZ_FORMAT_ERROR / XZ_OPTIONS_ERROR are non-XZ or unsupported-feature streams; XZ_DATA_ERROR / XZ_BUF_ERROR are corruption, truncation, or a stuck stream. XZ_MEM_ERROR and XZ_UNSUPPORTED_CHECK cannot occur in this build (XZ_PREALLOC allocates only at init; XZ_DEC_ANY_CHECK is off) and fall to the defensive default.

Parameters
[in]retxz-embedded return code (a failure code).
Returns
The bounded ra8_err_t for ret.
Return values
k_ra8_err_no_memDictionary over the scratch budget.
k_ra8_err_not_supportedNot XZ / unsupported check or filter.
k_ra8_err_validation_failedCorrupt, truncated, or stuck stream.
Precondition
ret is not XZ_OK / XZ_STREAM_END (caller-handled).
The failed session will be ended by the caller.
Postcondition
No state is modified (pure mapping).
The result is never k_ra8_ok.
Note
Thread-safe: pure mapping.
Since
Version 0.1.0

Definition at line 146 of file unarch_xz.c.

References k_ra8_err_no_mem, k_ra8_err_not_supported, and k_ra8_err_validation_failed.

Referenced by unarch_xz_stream_run().

◆ unarch_xz_magic()

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

Whether sig begins with the XZ stream header magic.

Pure signature probe over the six magic bytes (FD 37 7A 58 5A 00) 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 an XZ stream.
Return values
trueThe six magic bytes match.
falsesig is NULL, too short, or not XZ.
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 six bytes.
Note
Thread-safe: pure read.
See also
unarch_xz_unwrap()
Since
Version 0.1.0

Definition at line 77 of file unarch_xz.c.

References k_unarch_xz_sig_len, k_xz_magic_b0, k_xz_magic_b1, k_xz_magic_b2, k_xz_magic_b3, k_xz_magic_b4, k_xz_magic_b5, and memeq.

Referenced by comic_open_wrapped(), and internal_open_unwrapped().

◆ unarch_xz_stream_begin()

ra8_err_t unarch_xz_stream_begin ( unarch_xz_stream_t * xs,
void * scratch,
uint32_t scratch_len )
nodiscard

Begin a multi-call XZ decode session over a caller scratch.

Installs scratch as the XZ allocation arena and creates an XZ_PREALLOC decoder whose dictionary budget is scratch_len - k_unarch_xz_state_reserve: the dictionary is allocated once at init and never grows, and a stream declaring a larger dictionary is rejected during _run (no growth, no fallback).

Parameters
[out]xsSession to bind (non-NULL).
[in]scratchScratch buffer (non-NULL, 8-byte aligned).
[in]scratch_lenScratch length, > k_unarch_xz_state_reserve.
Returns
ra8_err_t Error code.
Return values
k_ra8_okSession live; feed it via _run.
k_ra8_err_null_ptrxs or scratch was NULL.
k_ra8_err_invalid_sizeScratch too small or misaligned.
k_ra8_err_busyAnother XZ session is in flight.
k_ra8_err_no_memDecoder-state allocation failed.
Precondition
No other XZ session is live (single-client pool).
scratch out-lives the session.
Postcondition
On k_ra8_ok, xs->live is true until unarch_xz_stream_end.
On any error the pool is released and xs is dead.
Note
Not thread-safe.
See also
unarch_xz_stream_run()
Since
Version 0.1.0

Definition at line 96 of file unarch_xz.c.

References unarch_xz_stream_t::dec, k_ra8_err_invalid_size, k_ra8_err_no_mem, k_ra8_ok, k_unarch_xz_state_reserve, unarch_xz_stream_t::live, RA8_CHECK_NULL_PTR, s_tag_xz, unarch_xz_pool_install(), and unarch_xz_pool_reset().

Referenced by unarch_xz_unwrap().

◆ unarch_xz_stream_end()

void unarch_xz_stream_end ( unarch_xz_stream_t * xs)

End a session: free the decoder and release the allocation pool.

Safe on a NULL or never-begun session (teardown paths call it unconditionally). After this the caller's scratch is dead storage again and a new session may begin.

Parameters
[in,out]xsSession to end (may be NULL).
Precondition
xs, when live, owns the installed pool.
No further _run calls follow on xs.
Postcondition
xs->live is false and the pool is uninstalled.
A subsequent unarch_xz_stream_begin may reuse the scratch.
Note
Not thread-safe.
See also
unarch_xz_stream_begin()
Since
Version 0.1.0

Definition at line 212 of file unarch_xz.c.

References unarch_xz_stream_t::dec, unarch_xz_stream_t::live, and unarch_xz_pool_reset().

Referenced by unarch_xz_unwrap().

◆ unarch_xz_stream_run()

ra8_err_t unarch_xz_stream_run ( unarch_xz_stream_t * xs,
const uint8_t * in,
size_t in_len,
size_t * in_used,
uint8_t * out,
size_t out_cap,
size_t * out_used,
bool * end )
nodiscard

Feed one input chunk through a live session, producing output.

One xz_dec_run pass: consumes up to in_len input bytes and writes up to out_cap output bytes, reporting both counts and whether the stream ended. The caller loops – charging its own ra8_decomp_budget_t per pass – until *end or an error. Returning with zero consumed and zero produced is legal decoder behaviour on tiny buffers; the caller's iteration budget bounds the loop regardless (the decoder itself also reports a stuck stream as a validation failure).

Parameters
[in,out]xsLive session (non-NULL).
[in]inInput chunk (non-NULL when in_len > 0).
[in]in_lenInput bytes available.
[out]in_usedReceives input bytes consumed (non-NULL).
[out]outOutput chunk buffer (non-NULL).
[in]out_capOutput capacity in bytes.
[out]out_usedReceives output bytes produced (non-NULL).
[out]endReceives true when the stream verified and ended (non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okPass completed; inspect the counts.
k_ra8_err_null_ptrA required pointer was NULL.
k_ra8_err_invalid_statexs is not live.
k_ra8_err_no_memDeclared dictionary exceeds the session's scratch budget.
k_ra8_err_not_supportedUnsupported format/check/filter.
k_ra8_err_validation_failedCorrupt / truncated stream, a failed integrity check, or a stuck stream.
Precondition
unarch_xz_stream_begin succeeded on xs.
out holds out_cap writable bytes.
Postcondition
On k_ra8_ok, out[0..*out_used) holds newly decoded bytes.
On any error the session must be ended (it is not recoverable).
Note
Not thread-safe.
See also
unarch_xz_stream_end()
Since
Version 0.1.0

Definition at line 168 of file unarch_xz.c.

References unarch_xz_stream_t::dec, internal_xz_map_err(), k_ra8_err_invalid_state, k_ra8_ok, unarch_xz_stream_t::live, RA8_CHECK_NULL_PTR, and s_tag_xz.

Referenced by internal_unwrap_pass().

◆ unarch_xz_unwrap()

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

Decode one whole XZ stream from a read seam into a caller arena.

Runs a full streaming session over [0, size) of the backing: input is fetched in small fixed chunks, each decode pass is charged against limits (iteration budget, output cap, ratio bound), and the stream's integrity check is verified before success. Fail-closed on everything else: truncation, corruption, an unsupported check/filter, a dictionary larger than the scratch budget, output overrun, and trailing bytes after the stream footer.

Parameters
[in]readByte reader over the XZ stream (non-NULL).
[in]ctxContext passed to read.
[in]sizeStream length in bytes (> 0).
[out]outDestination arena (non-NULL, out_cap bytes).
[in]out_capCapacity of out in bytes (> 0).
[in]scratchSession scratch (non-NULL, 8-byte aligned, > k_unarch_xz_state_reserve bytes).
[in]scratch_lenScratch length in bytes.
[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_okStream decoded and verified.
k_ra8_err_null_ptrA required pointer was NULL.
k_ra8_err_invalid_sizesize / out_cap is 0, or the scratch is undersized / misaligned.
k_ra8_err_invalid_arglimits has a zero field.
k_ra8_err_busyAnother XZ session is in flight.
k_ra8_err_no_memDecoder-state allocation failed, the declared dictionary exceeds the scratch budget, or out is too small for the stream.
k_ra8_err_not_supportedNot an XZ stream, or one using an unsupported check/filter.
k_ra8_err_validation_failedCorrupt / truncated / trailing-byte stream or a failed integrity check.
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
No other XZ session is live (single-threaded reader loop).
read serves offsets [0, size) of the stream.
Postcondition
On k_ra8_ok, out[0..*out_len) holds the decoded bytes.
On any error *out_len == 0; the pool is released either way.
Note
Not thread-safe.
See also
unarch_xz_stream_run()
Since
Version 0.1.0

Definition at line 351 of file unarch_xz.c.

References xz_unwrap_state_t::budget, xz_unwrap_state_t::ctx, xz_unwrap_state_t::in_off, internal_unwrap_pass(), internal_unwrap_reject_null(), k_ra8_err_invalid_size, k_ra8_err_validation_failed, k_ra8_ok, xz_unwrap_state_t::out, xz_unwrap_state_t::out_cap, ra8_decomp_budget_init(), xz_unwrap_state_t::read, xz_unwrap_state_t::size, xz_unwrap_state_t::total_out, unarch_xz_stream_begin(), unarch_xz_stream_end(), and xz_unwrap_state_t::xs.

Referenced by internal_unwrap().

Variable Documentation

◆ s_tag_xz

const char* const s_tag_xz = "unarch_xz"
static

Log tag for XZ-wrapper diagnostics.

Definition at line 37 of file unarch_xz.c.

Referenced by internal_unwrap_reject_null(), unarch_xz_stream_begin(), and unarch_xz_stream_run().