|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
The MISRA-C 2012 audit policy for ra8-firmware: why the project holds itself to a language subset, what the one available open-source checker can and cannot see, and how the remaining debt is stopped from growing.
The findings themselves are not in this document. .github/misra-baseline.txt owns the per-file, per-rule counts, and docs/qualification/MISRA_DEVIATIONS.md owns the formal deviation and tooling-gap records.
This firmware targets IEC 61508 SIL 3 and DO-178C up to Level B. Both expect a documented coding standard with mechanical enforcement of language-subset rules:
MISRA-C 2012 categorises every rule as Mandatory, Required or Advisory (sec. 5.1). For SIL 3 / Level B every mandatory and required rule must be either obeyed or formally deviated under sec. 5.2, which demands a rationale, a scope, an alternative mitigation, and review sign-off.
cppcheck is the only open-source MISRA-C 2012 checker. The commercial alternatives – LDRA Testbed, Helix QAC, Polyspace, Coverity – are explicitly out of scope here; see the deviation register's "Tooling policy" section and docs/CERTIFICATION_SCOPE.md. A downstream party who adopts this codebase for a regulated product is responsible for procuring their own qualified checker.
cppcheck's MISRA addon implements a subset of the rules: essentially all of the mandatory ones (mostly syntactic bans on goto, setjmp and dynamic memory, which are easy to detect), somewhat over half the required ones, and about half the advisory ones. The uncovered required rules are the ones that need whole-program type-flow analysis – the essential-type model – or interprocedural reasoning; the standard-library rules are checked syntactically only. Expect roughly two thirds of the mandatory-plus-required set to be detected. The remaining third is accepted as residual risk under IEC 61508-7 Annex D.7, "achievable assurance with available tools".
Two further limitations shape how the numbers read:
cppcheck 2.13's bundled Rule 9 MISRA add-on predates C23 6.7.10. Its empty-brace branch marks every = {} aggregate as under-braced, even though an empty initializer contains no initializer clause that could omit a nested brace. The audit therefore verifies the exact cppcheck 2.13.0 bytes for the MISRA driver, its Rule 9 helper, their Python data dependency, and the POSIX library model. It copies those four upstream assets into a disposable build directory, verifies the repository-owned patch, applies its one-predicate correction, and verifies the patched bytes before running them. The installed add-on and library model are never changed. Any tool-version, dependency, source-addon, library-model, patch, or patched-output drift fails closed.
The correction was measured across all 390 translation-unit dumps represented by a Rule 9.2 baseline row. The stock addon reported 2,107 unique file-and-line findings; the corrected addon reported five. A mechanical whole-initializer classifier traced all 2,102 removed sites to an actual empty {} construct, with zero additions and zero unexplained removals. The five non-empty, under-braced findings remained. The selftest independently exercises top-level, nested, designated-member, and array empty initializers. Each must remain quiet for Rules 9.2 and 9.4, while under-braced and excess-brace aggregates still raise Rule 9.2 and a duplicate designated member still raises Rule 9.4. It also proves that addon, dependency, and patch drift are rejected.
The same staged authority supplies cppcheck's POSIX declaration model to port/posix/src/fw_if_fs_posix_common.c only. Cppcheck 2.13 does not retain the hosted system-header declarations in its ordinary C11 dump, which creates four false Rule 17.3 findings. A global POSIX model also changes unrelated type inference, so the audit replaces only that translation unit's ordinary dump with a fresh dump using the digest-pinned, absolute staged posix.cfg path. The selftest proves the common exclusions, suppressions, include roots, and parser options are identical in both passes; exactly four Rule 17.3 findings disappear, the existing Rule 21.1 finding remains, and no other focused diagnostic changes.
The final pinned audit and its derived qualification records freeze the measured result. Relative to the 22,097-finding baseline, the C23 compatibility correction removes 2,102 modeled false positives, the targeted POSIX model removes four false Rule 17.3 findings, and reviewed source fixes remove 53 genuine findings. The freestanding-runtime reconciliation then adds 82 ratchet-held findings in the compiler-required ABI layer; those are covered by D-001, D-006, D-010, and D-012 through D-015. The committed 20,020-finding baseline records that exact population with zero bucket growth.
The audit covers first-party translation units under libs/, port/, tools/, and apps/, and excludes libs/third_party/. The repository-root tests/ and examples/ trees are outside this scan, while app-local tests under apps/ are in scope with their product code. Per-violation output and the raw checker logs land under build/misra/, regenerated on every run; none of it is committed.
The src//inc/ migration moved 116 test translation units from the repository-root tests/ tree into their owning apps/**/tests/ trees. That is a deliberate audit-scope expansion: app-local tests are product code for this policy and remain in scope. They were not excluded to make the gate pass.
The migration audit first rewrote only the old baseline paths and verified that the mapped baseline still contained exactly 20,976 findings in 3,011 file/rule rows. Comparing the pinned 2.13.0 scan against that count-preserving map separated the growth into two populations:
Final frozen refactors exposed seven additional file/rule buckets containing 16 findings of those same documented C23-parser false-positive classes. Their exact file/rule suppressions also burned down 84 already-baselined Rule 9.2 findings. Two real Rule 15.4 findings introduced by coverage seams were fixed in source; the Alphabet CLI split transferred nine D-001 findings from main.c to its private helper without changing the Rule 15.5 population.
The final pinned baseline therefore includes those 1,500 scope-expansion findings and the separately dispositioned D-001/D-010 additions; it does not label the latter as migration debt. Any simultaneous finding reductions remain ordinary ratchet burn-down and are retained when the baseline is regenerated.
The audit is enforced in CI as a ratchet (#240), not as a clean-run gate. That distinction is the whole design: it lets a large pre-existing debt burn down over time while making it impossible to add to.
scripts/checks/check_misra_deviations.py closes the loop from the other side: it re-derives every machine-checked claim in the deviation register from the baseline and the suppression list, so the register cannot drift away from the measurement it claims to describe.
Per MISRA-C:2012 sec. 5.2 a finding has exactly three possible outcomes:
The deviation register owns the formal deviations and tooling-gap records. Rules outside those records remain ratchet-held Code change debt; an index row's population is not blanket acceptance of every finding under that rule. The substantive records include:
| Rule | Disposition | Rationale |
|---|---|---|
| 15.5 single point of exit | Project deviation (D-001) | Single-exit conflicts with NASA Power-of-10 Rule 7 and with the RA8_RETURN_ON_ERROR early-return idiom that is the project's default error propagation. Mitigation: at least two pre/post-condition checks per function plus MC/DC, which gives equivalent assurance against missed cleanup paths. |
| 17.3 implicit declaration | Tooling gap (D-002) | cppcheck reports an implicit declaration when a header fails to parse on a C23 line and a downstream call therefore looks undeclared. |
| 9.2 initializer braces | Tooling gap (D-003) | The C23 = {} empty initializer is read as an under-braced aggregate initializer. |
| 12.1 operator precedence | Partial deviation + code change (D-004) | Implicit precedence accepted for * / over + -, unary over binary, member access over any, and postfix call over any. Redundant parentheses added everywhere else; clang-format will not re-flatten them. |
| 8.4 compatible declaration visible | Tooling gap (D-005) | Every hit traces to syntaxError on the [[nodiscard]] attribute of the matching public-header prototype, or to a third-party header deliberately excluded from the audit. The cross compiler rejects any real Rule 8.4 violation as a build error, so the source obeys the rule. |
| 11.6 pointer/integer conversion | Narrow project deviation (D-011) | The XZ caller-workspace installer converts one void* value to uintptr_t solely to reject an address that cannot satisfy the decoder arena's alignment contract. No integer is converted back to a pointer. |
| 21.1 reserved identifiers | Narrow project deviation (D-012) | Guarded first-party XZ and freestanding test adapters preserve the exact compiler/runtime spellings; no other Rule 21.1 findings are accepted. |
| 5.5 identifier/macro distinction | Narrow project deviation (D-013) | The freestanding runtime must expose compiler-required standard names, and its test-only aliases must select those implementations rather than hosted libc. |
| 21.2 reserved declarations | Narrow project deviation (D-014) | The freestanding foundation must declare the standard ABI functions it implements after removing newlib/libnosys from the target image. |
| 11.8 const-removing conversion | Narrow project deviation (D-015) | The standard memchr/string-search signatures require writable return types from const inputs; the six conversions are confined to the freestanding ABI bodies. |
A tooling-gap disposition is not a permanent excuse. Each carries an early-review trigger that fires when the pinned checker gains the capability it was missing; at that point the residual findings are re-audited and whatever survives is reclassified.
cppcheck-MISRA at roughly two-thirds rule coverage is this project's permanent MISRA enforcement stance, not an interim one. The project does not seek certification itself (see docs/CERTIFICATION_SCOPE.md); an adopter who does is responsible for their own qualified-tool re-audit.
| Asset | Purpose |
|---|---|
| just quality::local::gate misra | Registered CI entry point; runs the pinned audit, deviation-integrity checks, and ratchet comparison. |
| scripts/checks/misra_check.sh | Developer front end for the pinned audit; --check also runs the committed ratchet. |
| scripts/checks/misra_check_inner.sh | The audit itself. Writes build/misra/ and prints a per-rule tally. Invoked by just quality::local::misra. |
| scripts/checks/misra_ratchet.py | Ratchet comparator; --update regenerates the baseline. |
| scripts/checks/check_misra_deviations.py | Re-derives the deviation register's machine-checked claims from the baseline and the suppression list. |
| .github/misra-baseline.txt | Committed per-file-per-rule counts plus the generating cppcheck version. |
| .cppcheck-suppressions | Project-wide suppressions with justification comments. Every misra-c2012-* family here must be owned by the deviation register's suppression-ownership list, and that ownership is gated. |