|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Static stack-overflow is the silent equivalent of dynamic allocation: NASA Power-of-10 Rule 3 forbids malloc after init, but a 4 KB uint8_t buf[4096] on the stack of an interrupt handler will smash the next region of RAM just as effectively.
This document explains how this firmware demonstrates a per-function stack bound – a hard requirement for the project's IEC 61508 SIL 3 / DO-178C Level B claim.
The compiler can give us per-function frame sizes for free; the cost is wiring up the report and the gate.
cmake/ra8_warnings.cmake :: ra8_target_enable_project_warnings() attaches two flags to every project-owned C target:
N defaults to 2048 bytes and may be overridden per app:
Both flags are gated on COMPILE_LANG_AND_ID:C,GNU so the host unit-test build (which may use clang) is unaffected.
Each line of <file>.su has the form:
QUALIFIER is one of:
| Qualifier | Meaning |
|---|---|
| static | Frame size known at compile time. Safe. |
| bounded | Variable-length but with a compiler-provable upper bound. |
| dynamic | VLA or alloca(); size only known at runtime. Forbidden. |
| dynamic,bounded | VLA whose upper bound the compiler can prove. |
The project policy is: no dynamic frames anywhere (no VLAs, no alloca()). bounded is acceptable when the bound is enum-derived.
scripts/checks/stack_usage_check.py walks every .su file under any per-app build*/ tree beneath examples/ (apps nest 2-4 levels deep, e.g. examples/ek_ra8d2/hw_validated/hil/<app>/build/), parses every frame, and emits:
The script exits non-zero when any function in a critical-path module – ra8_isr, ra8_check, ra8_err, ra8_mpu, ra8_cgc, ra8_pfs – has a frame larger than 256 bytes or carries a dynamic qualifier. Soft violations elsewhere (frame > 2048 bytes, or dynamic in any non-critical TU) are reported but do not fail the script – the per-target -Wstack-usage=N warning is the build-time gate for those.
just quality::local::stack_usage builds every EVM-tier app (everything under examples/ek_ra8d2/) and then runs the aggregator over the result.
just quality::local::stack_usage regenerates the report. This section describes the shape the report always has, rather than what it said on one day.
The largest frames in the tree belong to the vendored miniz deflate / zip helpers, which link only into the book-reading apps. They are SOUP: the project vendors third-party libraries unmodified and writes integration shims around them, so the answer to a large upstream frame is the per-app STACK_USAGE_BYTES override that sizes the consuming app's main stack – not an edit to libs/third_party/.
The largest first-party frames are the RSIP protected-mode routines in libs/ra8_hal/src/ra8_rsip_protected.c, and that is deliberate: each holds an unwrapped key or modulus scratch buffer plus a full ra8_rsip_key_handle_t on the stack, so the secret material is scrubbed via p_scrub when the frame unwinds. Moving those buffers into .bss would either persist the secret across calls or require an explicit clear-on-exit path that doubles the attack surface. Each carries an inline RA8_STACK_BUDGET(N) matching its .su value and a @par Stack-budget deviation: block that says why.
No function anywhere in the report carries a dynamic qualifier, because one is a hard failure of the gate below: NASA Power-of-10 Rule 3 forbids VLAs and alloca() regardless of frame size.
scripts/git/pre-commit invokes scripts/checks/stack_usage_check.py --strict --quiet on every commit. Behaviour:
If a function legitimately needs more than its module's budget (e.g. a one-shot init routine that builds a large config struct on the stack rather than holding it in .bss), the deviation MUST be recorded inline using the RA8_STACK_BUDGET(N) annotation macro from libs/ra8_core/inc/ra8_stack_budget.h. Place the marker as the first statement inside the function body so the preceding Doxygen block remains adjacent to the function signature for doxy_audit.py:
The macro itself is a no-op marker (it expands to nothing); its purpose is to:
A deviation must include:
A deviation does not suppress the -Wstack-usage=N warning – the per-app STACK_USAGE_BYTES budget must also be raised, and the raise must be justified in the per-app CMakeLists.txt.