|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Unified decompression-limits policy – budget charging and checks. More...
#include "ra8_decomp_limits.h"#include <string.h>#include "ra8_attributes.h"#include "ra8_check.h"Go to the source code of this file.
Enumerations | |
| enum | priv_zip_preflight_t : uint32_t { k_priv_zip_eocd_bytes = 22U , k_priv_zip_comment_max = 65535U , k_priv_zip_scan_chunk = 512U , k_priv_zip_sig_bytes = 4U , k_priv_zip_entries_offset = 10U , k_priv_zip_comment_offset = 20U } |
| Fixed classic-ZIP EOCD geometry used by the entry-cap preflight. More... | |
| enum | priv_zip_signature_t : uint8_t { k_priv_zip_sig_p = 0x50U , k_priv_zip_sig_k = 0x4BU , k_priv_zip_sig_eocd = 0x05U , k_priv_zip_sig_fixed = 0x06U } |
| Classic ZIP EOCD signature bytes. More... | |
Functions | |
| ra8_decomp_limits_t | ra8_decomp_limits_default (void) |
| The owner-approved default decompression policy. | |
| static bool | internal_limits_usable (const ra8_decomp_limits_t *lim) |
| Whether every field of a policy is non-zero (usable as a bound). | |
| static uint64_t | internal_ratio_bound (const ra8_decomp_limits_t *lim, uint64_t in_total) |
| The saturating ratio bound in * max_ratio + grace for a policy. | |
| ra8_err_t | ra8_decomp_budget_init (ra8_decomp_budget_t *b, const ra8_decomp_limits_t *limits) |
| Bind a budget to a policy (or the default policy) and zero it. | |
| ra8_err_t | ra8_decomp_budget_charge_output (ra8_decomp_budget_t *b, uint64_t in_total, uint64_t out_delta) |
| Charge decompressed output against the cap and ratio bounds. | |
| ra8_err_t | ra8_decomp_budget_charge_entry (ra8_decomp_budget_t *b) |
| Charge one enumerated archive entry against the entry cap. | |
| ra8_err_t | ra8_decomp_budget_charge_iter (ra8_decomp_budget_t *b) |
| Charge one decode-loop turn against the iteration budget. | |
| ra8_err_t | ra8_decomp_budget_enter (ra8_decomp_budget_t *b) |
| Enter one stacked decode layer (nesting-depth guard). | |
| void | ra8_decomp_budget_leave (ra8_decomp_budget_t *b) |
| Leave one stacked decode layer (balances ra8_decomp_budget_enter). | |
| ra8_err_t | ra8_decomp_check_declared (const ra8_decomp_limits_t *limits, uint64_t comp_size, uint64_t out_size) |
| Header-level check of a member's declared sizes against a policy. | |
| static uint16_t | internal_zip_u16 (const uint8_t *bytes) |
| Decode one little-endian 16-bit ZIP field. | |
| static bool | internal_zip_scan_window_for_eocd (ra8_decomp_read_fn read, void *ctx, const uint8_t *chunk, uint64_t start, size_t count, uint64_t archive_size, ra8_err_t *out_status) |
| Search one loaded scan window backward for a verified ZIP EOCD record. | |
| ra8_err_t | ra8_decomp_zip_entry_preflight (ra8_decomp_read_fn read, void *ctx, uint64_t archive_size) |
| Reject an over-cap ZIP from its EOCD before directory allocation. | |
Variables | |
| static const char *const | s_tag_decomp = "ra8_decomp" |
| Log tag for decompression-policy diagnostics. | |
| static const uint8_t | s_zip_eocd_signature [k_priv_zip_sig_bytes] |
| Classic ZIP end-of-central-directory signature bytes. | |
Unified decompression-limits policy – budget charging and checks.
Implementation of the one enforcement seam every archive / stream decoder shares (see ra8_decomp_limits.h). Charging arithmetic is saturating and overflow-checked so hostile 64-bit header values cannot wrap a bound: the ratio limit in * max_ratio + grace is computed with an explicit divide-guard and saturates to UINT64_MAX, at which point only the absolute output cap governs (which is exactly the intent: a huge honest input is bounded by the cap, not the quotient).
Every decision in this file is a single condition on purpose: the policy is the most security-load-bearing code in the content path, so each bound is tested independently (and carries no compound-decision MC/DC burden).
Definition in file ra8_decomp_limits.c.
| enum priv_zip_preflight_t : uint32_t |
Fixed classic-ZIP EOCD geometry used by the entry-cap preflight.
Definition at line 37 of file ra8_decomp_limits.c.
| enum priv_zip_signature_t : uint8_t |
Classic ZIP EOCD signature bytes.
| Enumerator | |
|---|---|
| k_priv_zip_sig_p | ASCII P. |
| k_priv_zip_sig_k | ASCII K. |
| k_priv_zip_sig_eocd | EOCD record identifier. |
| k_priv_zip_sig_fixed | EOCD fixed suffix. |
Definition at line 47 of file ra8_decomp_limits.c.
|
static |
Whether every field of a policy is non-zero (usable as a bound).
A zero cap is always a configuration bug: it would reject all input (caps) or create ambiguity (grace), so binding such a policy is refused outright.
| [in] | lim | Policy to inspect (non-NULL). |
| true | The policy is usable. |
| false | At least one field is zero. |
lim is non-NULL (caller-guarded). lim is fully initialised (no indeterminate fields). lim. Definition at line 88 of file ra8_decomp_limits.c.
References ra8_decomp_limits_t::max_depth, ra8_decomp_limits_t::max_entries, ra8_decomp_limits_t::max_iterations, ra8_decomp_limits_t::max_output_bytes, ra8_decomp_limits_t::max_ratio, RA8_INTERNAL, and ra8_decomp_limits_t::ratio_grace_bytes.
Referenced by ra8_decomp_budget_init().
|
static |
The saturating ratio bound in * max_ratio + grace for a policy.
Guards both the multiply and the add against 64-bit wrap: a product or sum that would overflow saturates to UINT64_MAX, deliberately disabling the ratio test for astronomically large honest inputs (the absolute output cap still governs those).
| [in] | lim | Policy supplying max_ratio / ratio_grace_bytes. |
| [in] | in_total | Compressed bytes consumed (or declared). |
| UINT64_MAX | When the bound saturates (ratio test disabled). |
lim is non-NULL with non-zero max_ratio (caller-guarded). in_total is the untrusted input size; any value is safe. Definition at line 128 of file ra8_decomp_limits.c.
References ra8_decomp_limits_t::max_ratio, RA8_INTERNAL, and ra8_decomp_limits_t::ratio_grace_bytes.
Referenced by ra8_decomp_budget_charge_output(), and ra8_decomp_check_declared().
|
static |
Search one loaded scan window backward for a verified ZIP EOCD record.
Walks chunk from its high end looking for the EOCD signature, re-reads the full record at each candidate, and accepts the first one whose declared comment length lands exactly on archive_size. A signature match that fails re-read or comment-length validation is a hash collision in the scan window, not the true record, so the scan continues past it.
| [in] | read | Caller-owned positioned-read callback. |
| [in,out] | ctx | Opaque context forwarded to read. |
| [in] | chunk | Bytes already loaded for the current scan window. |
| [in] | start | Absolute archive offset of chunk[0]. |
| [in] | count | Number of leading bytes of chunk to search. |
| [in] | archive_size | Total archive size in bytes. |
| [out] | out_status | Preflight result, valid only when this returns true. |
| true | A verified record was found; out_status holds its result. |
| false | No verified record in this window; the caller should rescan an earlier window. |
chunk holds at least count plus the signature length bytes. out_status is non-NULL. out_status is written if and only if this returns true. chunk is not modified; each candidate is re-read into a private buffer. read mutates shared state; the reader owns that. Definition at line 274 of file ra8_decomp_limits.c.
References internal_zip_u16(), k_priv_zip_comment_offset, k_priv_zip_entries_offset, k_priv_zip_eocd_bytes, k_priv_zip_sig_bytes, k_ra8_decomp_def_max_entries, k_ra8_err_decomp_entries, k_ra8_ok, memcmp(), RA8_INTERNAL, and s_zip_eocd_signature.
Referenced by ra8_decomp_zip_entry_preflight().
|
static |
Decode one little-endian 16-bit ZIP field.
Combines two already-bounds-checked bytes without an unaligned load.
| [in] | bytes | Address of two readable bytes. |
| 0 | The encoded field is zero. |
bytes is non-NULL. bytes. Definition at line 243 of file ra8_decomp_limits.c.
References RA8_INTERNAL.
Referenced by internal_zip_scan_window_for_eocd().
|
nodiscard |
Charge one enumerated archive entry against the entry cap.
Walkers call this once per member they index (ZIP central directory record, RAR file block, tar header). The many-tiny-entries bomb is rejected the moment the count crosses max_entries.
| [in,out] | b | Initialised budget (non-NULL). |
| k_ra8_ok | Entry recorded within the cap. |
| k_ra8_err_null_ptr | b was NULL. |
| k_ra8_err_decomp_entries | The entry count now exceeds max_entries. |
b was initialised by ra8_decomp_budget_init. Definition at line 176 of file ra8_decomp_limits.c.
References k_ra8_err_decomp_entries, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag_decomp.
Referenced by internal_finish_member(), and priv_comic_cbr_open().
|
nodiscard |
Charge one decode-loop turn against the iteration budget.
The NASA P10 Rule 2 backstop for loops whose trip count depends on untrusted bytes: each pass through such a loop charges one turn, so a stream engineered to stall (no input consumed, no output produced) still terminates within max_iterations.
| [in,out] | b | Initialised budget (non-NULL). |
| k_ra8_ok | Turn recorded within the budget. |
| k_ra8_err_null_ptr | b was NULL. |
| k_ra8_err_decomp_iterations | The turn count now exceeds max_iterations. |
b was initialised by ra8_decomp_budget_init. Definition at line 186 of file ra8_decomp_limits.c.
References k_ra8_err_decomp_iterations, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag_decomp.
Referenced by internal_inflate_pass(), internal_unwrap_pass(), and unarch_tar_next().
|
nodiscard |
Charge decompressed output against the cap and ratio bounds.
Adds out_delta to the produced-bytes counter, records the input consumed so far, then enforces (1) the per-unit output cap and (2) the bomb bound out <= in * max_ratio + grace. Call it after every emitted chunk so a bomb is caught within one chunk of the bound, long before RAM or CPU is exhausted.
| [in,out] | b | Initialised budget (non-NULL). |
| [in] | in_total | Compressed bytes consumed so far (monotonic). |
| [in] | out_delta | Newly produced decompressed bytes (may be 0). |
| k_ra8_ok | Charge recorded; both bounds hold. |
| k_ra8_err_null_ptr | b was NULL. |
| k_ra8_err_decomp_output_cap | Output now exceeds max_output_bytes. |
| k_ra8_err_decomp_ratio | Output now exceeds the ratio bound. |
b was initialised by ra8_decomp_budget_init. in_total is non-decreasing across calls on the same budget. out_delta. Definition at line 158 of file ra8_decomp_limits.c.
References internal_ratio_bound(), k_ra8_err_decomp_output_cap, k_ra8_err_decomp_ratio, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag_decomp.
Referenced by internal_inflate_pass(), and internal_unwrap_pass().
|
nodiscard |
Enter one stacked decode layer (nesting-depth guard).
Charge before constructing an inner decoder over an outer one (e.g. the tar walker over a gzip stream view). The archive-in-archive bomb is rejected before the inner layer does any work.
| [in,out] | b | Initialised budget (non-NULL). |
| k_ra8_ok | Depth recorded within max_depth. |
| k_ra8_err_null_ptr | b was NULL. |
| k_ra8_err_decomp_depth | The depth now exceeds max_depth. |
b was initialised by ra8_decomp_budget_init. Definition at line 196 of file ra8_decomp_limits.c.
References k_ra8_err_decomp_depth, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag_decomp.
|
nodiscard |
Bind a budget to a policy (or the default policy) and zero it.
Copies limits (or the default when NULL) into b and clears every counter. Rejects a policy with any zero field: a zero cap is always a configuration bug, never a meaningful bound.
| [out] | b | Budget to initialise (non-NULL). |
| [in] | limits | Policy to enforce, or NULL for the default policy. |
| k_ra8_ok | Budget zeroed and bound to a valid policy. |
| k_ra8_err_null_ptr | b was NULL. |
| k_ra8_err_invalid_arg | limits has a zero field. |
b addresses a writable ra8_decomp_budget_t. limits, when non-NULL, has every field non-zero. b is left zeroed (unusable until re-initialised).Definition at line 142 of file ra8_decomp_limits.c.
References internal_limits_usable(), k_ra8_err_invalid_arg, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_decomp_limits_default(), and s_tag_decomp.
Referenced by unarch_gzip_unwrap(), unarch_tar_open(), and unarch_xz_unwrap().
| void ra8_decomp_budget_leave | ( | ra8_decomp_budget_t * | b | ) |
Leave one stacked decode layer (balances ra8_decomp_budget_enter).
Decrements the depth counter; a NULL budget or an already-zero depth is ignored (teardown paths call this unconditionally).
| [in,out] | b | Budget to unwind (may be NULL). |
b, when non-NULL, was initialised by ra8_decomp_budget_init. b or zero depth left everything unchanged.Definition at line 206 of file ra8_decomp_limits.c.
|
nodiscard |
Header-level check of a member's declared sizes against a policy.
Rejects a lying or hostile header before any decoding: the declared decompressed size must fit the per-unit output cap and the ratio bound relative to the declared compressed size. Used by every walker at index time (ZIP central directory, RAR block header, tar size field) so hostile members cost O(1).
| [in] | limits | Policy to check against (non-NULL, validated fields). |
| [in] | comp_size | Declared compressed (packed) size in bytes. |
| [in] | out_size | Declared decompressed (unpacked) size in bytes. |
| k_ra8_ok | Both declared sizes are within policy. |
| k_ra8_err_null_ptr | limits was NULL. |
| k_ra8_err_decomp_output_cap | out_size exceeds max_output_bytes. |
| k_ra8_err_decomp_ratio | out_size exceeds the ratio bound. |
limits has every field non-zero (policy from ra8_decomp_limits_default or validated by ra8_decomp_budget_init). Definition at line 218 of file ra8_decomp_limits.c.
References internal_ratio_bound(), k_ra8_err_decomp_output_cap, k_ra8_err_decomp_ratio, k_ra8_ok, ra8_decomp_limits_t::max_output_bytes, RA8_CHECK_NULL_PTR, and s_tag_decomp.
Referenced by internal_add_entry(), internal_add_member(), internal_finish_member(), internal_read_page(), internal_require_jof(), priv_epub_zip_guard_entry(), and priv_viewer_open_jof().
|
nodiscard |
The owner-approved default decompression policy.
Builds a ra8_decomp_limits_t from the ra8_decomp_defaults_t constants. This is the ONE policy every production decoder runs under; tests tighten copies of it to exercise breach paths with small fixtures.
| ra8_decomp_limits_t | Populated from ra8_decomp_defaults_t. |
Definition at line 60 of file ra8_decomp_limits.c.
References k_ra8_decomp_def_max_depth, k_ra8_decomp_def_max_entries, k_ra8_decomp_def_max_iters, k_ra8_decomp_def_max_ratio, k_ra8_decomp_def_output_bytes, k_ra8_decomp_def_ratio_grace, ra8_decomp_limits_t::max_depth, ra8_decomp_limits_t::max_entries, ra8_decomp_limits_t::max_iterations, ra8_decomp_limits_t::max_output_bytes, ra8_decomp_limits_t::max_ratio, and ra8_decomp_limits_t::ratio_grace_bytes.
Referenced by internal_add_entry(), internal_add_member(), internal_require_jof(), priv_comic_cbr_open(), priv_comic_cbz_open(), priv_epub_zip_guard_archive(), priv_epub_zip_guard_entry(), priv_viewer_open_jof(), ra8_decomp_budget_init(), and ra8_viewer_reader_bind().
|
nodiscard |
Reject an over-cap ZIP from its EOCD before directory allocation.
Scans only the bounded classic-ZIP comment window in fixed chunks. A valid EOCD whose total-entry field exceeds the shared policy is rejected before a bounded format allocator can obscure the cause with a generic allocation or validation failure.
| [in] | read | Seekable container callback. |
| [in] | ctx | Opaque callback context. |
| [in] | archive_size | Exact ZIP byte length. |
| k_ra8_ok | No valid over-cap EOCD was found; the ZIP decoder remains authoritative. |
| k_ra8_err_null_ptr | read was NULL. |
| k_ra8_err_decomp_entries | The EOCD declares more than 4096 entries. |
archive_size is the same size later supplied to the ZIP decoder. Definition at line 305 of file ra8_decomp_limits.c.
References internal_zip_scan_window_for_eocd(), k_priv_zip_comment_max, k_priv_zip_eocd_bytes, k_priv_zip_scan_chunk, k_priv_zip_sig_bytes, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag_decomp.
Referenced by epub_open(), epub_open_streamed(), and priv_comic_cbz_open().
|
static |
Log tag for decompression-policy diagnostics.
Definition at line 31 of file ra8_decomp_limits.c.
Referenced by ra8_decomp_budget_charge_entry(), ra8_decomp_budget_charge_iter(), ra8_decomp_budget_charge_output(), ra8_decomp_budget_enter(), ra8_decomp_budget_init(), ra8_decomp_check_declared(), and ra8_decomp_zip_entry_preflight().
|
static |
Classic ZIP end-of-central-directory signature bytes.
Definition at line 55 of file ra8_decomp_limits.c.
Referenced by internal_zip_scan_window_for_eocd().