ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_decomp_limits.c
Go to the documentation of this file.
1
23#include "ra8_decomp_limits.h"
24
25#include <string.h>
26
27#include "ra8_attributes.h"
28#include "ra8_check.h"
29
31static const char* const s_tag_decomp = "ra8_decomp";
32
45
47typedef enum : uint8_t {
53
59
71
89{
90 if (lim->max_output_bytes == 0U) {
91 return false;
92 }
93 if (lim->max_ratio == 0U) {
94 return false;
95 }
96 if (lim->ratio_grace_bytes == 0U) {
97 return false;
98 }
99 if (lim->max_entries == 0U) {
100 return false;
101 }
102 if (lim->max_iterations == 0U) {
103 return false;
104 }
105 if (lim->max_depth == 0U) {
106 return false;
107 }
108 return true;
109}
110
128RA8_INTERNAL static uint64_t internal_ratio_bound(const ra8_decomp_limits_t* lim, uint64_t in_total)
129{
130 const uint64_t ratio = (uint64_t)lim->max_ratio;
131 if (in_total > (UINT64_MAX / ratio)) {
132 return UINT64_MAX; /* product would wrap: saturate (output cap governs) */
133 }
134 const uint64_t product = in_total * ratio;
135 const uint64_t grace = (uint64_t)lim->ratio_grace_bytes;
136 if (product > (UINT64_MAX - grace)) {
137 return UINT64_MAX; /* sum would wrap: saturate (output cap governs) */
138 }
139 return product + grace;
140}
141
143{
144 RA8_CHECK_NULL_PTR(b, s_tag_decomp, "budget init: null budget");
145 *b = (ra8_decomp_budget_t){};
146 if (limits == nullptr) {
147 b->limits = ra8_decomp_limits_default();
148 return k_ra8_ok;
149 }
150 if (!internal_limits_usable(limits)) {
152 }
153 b->limits = *limits;
154 return k_ra8_ok;
155}
156
158ra8_decomp_budget_charge_output(ra8_decomp_budget_t* b, uint64_t in_total, uint64_t out_delta)
159{
160 RA8_CHECK_NULL_PTR(b, s_tag_decomp, "charge output: null budget");
161 if (out_delta > (UINT64_MAX - b->out_bytes)) {
162 b->out_bytes = UINT64_MAX; /* saturate: any wrap-sized delta is a breach */
163 } else {
164 b->out_bytes += out_delta;
165 }
166 b->in_bytes = in_total;
167 if (b->out_bytes > b->limits.max_output_bytes) {
169 }
170 if (b->out_bytes > internal_ratio_bound(&b->limits, in_total)) {
172 }
173 return k_ra8_ok;
174}
175
177{
178 RA8_CHECK_NULL_PTR(b, s_tag_decomp, "charge entry: null budget");
179 if (b->entries >= b->limits.max_entries) {
181 }
182 b->entries += 1U;
183 return k_ra8_ok;
184}
185
187{
188 RA8_CHECK_NULL_PTR(b, s_tag_decomp, "charge iter: null budget");
189 if (b->iters >= b->limits.max_iterations) {
191 }
192 b->iters += 1U;
193 return k_ra8_ok;
194}
195
197{
198 RA8_CHECK_NULL_PTR(b, s_tag_decomp, "enter: null budget");
199 if (b->depth >= b->limits.max_depth) {
201 }
202 b->depth = (uint8_t)(b->depth + 1U);
203 return k_ra8_ok;
204}
205
207{
208 if (b == nullptr) {
209 return; /* teardown paths call unconditionally */
210 }
211 if (b->depth == 0U) {
212 return; /* unbalanced leave: ignore rather than wrap */
213 }
214 b->depth = (uint8_t)(b->depth - 1U);
215}
216
218ra8_decomp_check_declared(const ra8_decomp_limits_t* limits, uint64_t comp_size, uint64_t out_size)
219{
220 RA8_CHECK_NULL_PTR(limits, s_tag_decomp, "check declared: null limits");
221 if (out_size > limits->max_output_bytes) {
223 }
224 if (out_size > internal_ratio_bound(limits, comp_size)) {
226 }
227 return k_ra8_ok;
228}
229
243RA8_INTERNAL static uint16_t internal_zip_u16(const uint8_t* bytes)
244{
245 return (uint16_t)((uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8U));
246}
247
275 void* ctx,
276 const uint8_t* chunk,
277 uint64_t start,
278 size_t count,
279 uint64_t archive_size,
280 ra8_err_t* out_status)
281{
282 uint8_t eocd[k_priv_zip_eocd_bytes];
283 for (size_t i = count; i > 0U; --i) {
284 const size_t at = i - 1U;
285 if (memcmp(&chunk[at], s_zip_eocd_signature, k_priv_zip_sig_bytes) != 0) {
286 continue;
287 }
288 const uint64_t position = start + (uint64_t)at;
289 if (read(ctx, position, eocd, sizeof(eocd)) != sizeof(eocd)) {
290 continue;
291 }
292 const uint16_t comment = internal_zip_u16(&eocd[k_priv_zip_comment_offset]);
293 if ((position + sizeof(eocd) + (uint64_t)comment) != archive_size) {
294 continue;
295 }
296 const uint16_t entries = internal_zip_u16(&eocd[k_priv_zip_entries_offset]);
297 *out_status = ((uint32_t)entries > (uint32_t)k_ra8_decomp_def_max_entries)
299 : k_ra8_ok;
300 return true;
301 }
302 return false;
303}
304
305ra8_err_t ra8_decomp_zip_entry_preflight(ra8_decomp_read_fn read, void* ctx, uint64_t archive_size)
306{
307 RA8_CHECK_NULL_PTR(read, s_tag_decomp, "zip preflight: null reader");
308 if (archive_size < (uint64_t)k_priv_zip_eocd_bytes) {
309 return k_ra8_ok;
310 }
311 const uint64_t candidates = archive_size - (uint64_t)k_priv_zip_eocd_bytes + 1U;
312 const uint64_t window = (uint64_t)k_priv_zip_comment_max + 1U;
313 const uint64_t lower = (candidates > window) ? (candidates - window) : 0U;
314 uint64_t end = candidates;
315 uint8_t chunk[k_priv_zip_scan_chunk + k_priv_zip_sig_bytes - 1U];
316 while (end > lower) {
317 const uint64_t start = ((end - lower) > (uint64_t)k_priv_zip_scan_chunk)
318 ? (end - (uint64_t)k_priv_zip_scan_chunk)
319 : lower;
320 const size_t count = (size_t)(end - start);
321 if (read(ctx, start, chunk, count + k_priv_zip_sig_bytes - 1U) !=
322 count + k_priv_zip_sig_bytes - 1U) {
323 return k_ra8_ok;
324 }
325 ra8_err_t status = k_ra8_ok;
326 if (internal_zip_scan_window_for_eocd(read, ctx, chunk, start, count, archive_size, &status)) {
327 return status;
328 }
329 end = start;
330 }
331 return k_ra8_ok;
332}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
static uint16_t internal_zip_u16(const uint8_t *bytes)
Decode one little-endian 16-bit ZIP field.
priv_zip_signature_t
Classic ZIP EOCD signature bytes.
@ 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.
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.
static bool internal_limits_usable(const ra8_decomp_limits_t *lim)
Whether every field of a policy is non-zero (usable as a bound).
ra8_decomp_limits_t ra8_decomp_limits_default(void)
The owner-approved default decompression policy.
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.
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.
priv_zip_preflight_t
Fixed classic-ZIP EOCD geometry used by the entry-cap preflight.
@ k_priv_zip_eocd_bytes
Fixed EOCD bytes before its comment.
@ k_priv_zip_entries_offset
Total-entry field offset in EOCD.
@ k_priv_zip_scan_chunk
Candidate offsets inspected per read.
@ k_priv_zip_sig_bytes
Bytes in the EOCD signature.
@ k_priv_zip_comment_offset
Comment-length field offset in EOCD.
@ k_priv_zip_comment_max
Maximum classic ZIP comment bytes.
static const uint8_t s_zip_eocd_signature[k_priv_zip_sig_bytes]
Classic ZIP end-of-central-directory signature bytes.
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_budget_charge_entry(ra8_decomp_budget_t *b)
Charge one enumerated archive entry against the entry cap.
static const char *const s_tag_decomp
Log tag for decompression-policy diagnostics.
ra8_err_t ra8_decomp_budget_charge_iter(ra8_decomp_budget_t *b)
Charge one decode-loop turn against the iteration budget.
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.
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_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.
Unified decompression-limits policy: one bound set for every decoder.
size_t(* ra8_decomp_read_fn)(void *ctx, uint64_t offset, void *buf, size_t len)
Positioned reader used by bounded container preflights.
@ k_ra8_decomp_def_max_iters
Loop iteration budget (1 Mi).
@ k_ra8_decomp_def_ratio_grace
Additive ratio grace (64 KiB).
@ k_ra8_decomp_def_max_depth
Stacked decode-layer cap.
@ k_ra8_decomp_def_max_entries
Per-archive entry cap.
@ k_ra8_decomp_def_output_bytes
Per-unit output cap (64 MiB).
@ k_ra8_decomp_def_max_ratio
Output:input ratio bound.
@ k_ra8_err_decomp_ratio
Compression ratio bound breached (ra8_decomp_limits_t).
Definition ra8_err.h:499
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_decomp_output_cap
Decompression output cap breached (ra8_decomp_limits_t).
Definition ra8_err.h:489
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_decomp_iterations
Decode-loop iteration budget exhausted (ra8_decomp_limits_t).
Definition ra8_err.h:531
@ k_ra8_err_decomp_depth
Container nesting-depth cap breached (ra8_decomp_limits_t).
Definition ra8_err.h:520
@ k_ra8_err_decomp_entries
Archive entry-count cap breached (ra8_decomp_limits_t).
Definition ra8_err.h:509
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
Running consumption tracker charged by a decoder against its policy.
One decompression policy: the five resource bounds decoders enforce.
uint32_t max_ratio
Output:input ratio bound (multiplier).
uint32_t ratio_grace_bytes
Additive output grace before ratio applies.
uint32_t max_iterations
Per-decode-loop iteration budget.
uint32_t max_entries
Per-archive enumerated-entry cap.
uint8_t max_depth
Stacked decode-layer (nesting) cap.
uint64_t max_output_bytes
Per-decode-unit output cap, bytes.