|
Safe Data API
CRC-protected (optionally redundant) data containers for Zephyr
|
|
v0.0-dev+gbb519af | |
Generic Zephyr facility for protecting a variable or data structure with an integrity tag (CRC), an optional redundant shadow copy, and a mutex — so that data integrity is guaranteed by construction rather than by remembering to call the right function at the right time.
The repository is also a self-contained example of generated, auditable safety evidence. Its requirements, annotated tests and twister results are rendered into a requirement specification, a test specification, a test report and a traceability matrix (see doc/README.md). The published documentation (without the test report for now) is at https://tiacsys.github.io/safety-toolbox/.
In a safety-critical context, RAM and stored values can be corrupted by SEUs, stack overflow, rogue DMA, or plain bugs. A container bundles the data with an integrity tag and serialises every access so that:
The lock is held across the whole transaction; the tag is resealed on success. If the mutator returns an error the change is rolled back (when the redundant shadow is enabled) and the error is propagated.
The ergonomic replacement for the original "checkout a pointer / remember to update" idea — but the lock is held for the whole block and the tag is resealed automatically at the end.
Do not return / break / goto out of a SAFE_SECTION block — that would skip the reseal + unlock. continue exits the block cleanly.
The mutex embedded in each container (the default) serialises every access, so a reader thread can never observe a half-finished update from a writer thread — each SAFE_READ snapshot is internally consistent and CRC-valid. See samples/producer_consumer for a runnable demo: a producer mutates a multi-field struct with an invariant (sum == x + y) via SAFE_UPDATE while a consumer validates snapshots via SAFE_READ; the invariant holds on every read.
If an audited low-level path writes the storage directly, SAFE_COMMIT reseals the tag (and refreshes the shadow) over the current contents under the lock:
| Option | Default | Meaning |
|---|---|---|
| SAFE_DATA | n | Enable the library (selects CRC). |
| SAFE_DATA_LEVEL_BASIC / _LEVEL_ENHANCED / _LEVEL_STRICT | BASIC | Integrity-level preset. ENHANCED implies redundancy + statistics; STRICT additionally implies boot self-test + checked writes and removes SAFE_COMMIT/SAFE_SECTION from the API surface. Implied options remain individually overridable. |
| SAFE_DATA_CRC32_IEEE / _CRC16_CCITT / _CRC8_CCITT | CRC32 | Integrity-tag algorithm. A compile-time bound ties tag strength to payload size (CRC-8 ≤ 16 B, CRC-16 ≤ 256 B). |
| SAFE_DATA_LOCKING | y | Embed a k_mutex in each container and serialise access. Disable to drop the mutex when a container is only touched from one context (caller then owns mutual exclusion). |
| SAFE_DATA_LOCK_TIMEOUT_MS | 0 | Bound on lock acquisition; on expiry the operation returns -ETIMEDOUT (counted + reported). 0 = wait forever. |
| SAFE_DATA_REDUNDANT | n | Keep an inverted shadow copy; enables recovery + RMW rollback (2× payload RAM). |
| SAFE_DATA_STATS | n | Global fault/recovery counters via safe_data_stats_get() — the observability hook for a health monitor. |
| SAFE_DATA_WRITE_CHECKS_OLD | n | Verify old contents before overwriting (observe-only; corruption is reported, the write still succeeds). |
| SAFE_DATA_SELFTEST (+ _SELFTEST_BOOT) | n | safe_data_selftest(): CRC known-answer test + seal/corrupt/detect/recover round trip. _BOOT runs it from SYS_INIT and panics on failure. |
| SAFE_DATA_ALLOW_UNCHECKED_COMMIT | y (n at STRICT) | Keep SAFE_COMMIT (reseal without verify) in the API surface. |
| SAFE_DATA_GNU_EXTENSIONS | y (n at STRICT) | Keep SAFE_SECTION (statement expressions, __typeof__) in the API surface. |
| SAFE_DATA_FAULT_RETURN / _FAULT_HANDLER / _FAULT_PANIC | RETURN | Control-flow reaction to an unrecoverable fault. Independently of it, a callback registered via safe_data_fault_handler_register() receives all integrity events (recoveries, timeouts, overwrites, clamped mutator returns). The callback may run with the container lock held: it must not block or touch safe containers. |
Return codes: 0 success, -EILSEQ unrecoverable corruption, -EINVAL bad args, SAFE_DATA_RECOVERED (-EAGAIN) for a shadow-coverable fault — from safe_data_verify() it means recoverable (pure check, payload untouched); from safe_data_verify_repair() (lock-held, used internally by read/update/SAFE_SECTION) it means repaired in place.
The repository is its own west manifest (west.yml): Zephyr at a release tag plus the zdocs documentation engine, nothing else.
All commands below run from the workspace root (toolbox-ws/).
The samples are self-contained applications and add this repository as a module themselves. On a 64-bit host without 32-bit multilib (e.g. aarch64), use native_sim/native/64 in place of native_sim.
Two test applications, seven twister scenarios. tests/safe_data runs in four (default, detection-only without the shadow copy, bounded locking, and the STRICT preset). tests/safe_data_fault runs in three, one per unrecoverable-fault reaction (return, handler, panic):
The twister output directory is also the input to the generated test report (see doc/README.md). The report build takes it as -DZDOCS_TWISTER_OUT=<dir>. Without a toolchain, pass the committed fixture doc/_fixtures/twister; after a fresh run, pass that run's output directory instead.
Add the repository to your own manifest, or pass it on the command line:
affirmatrix.yaml configures the affirmatrix evidence-graph tool for this repository. affirmatrix builds a hash-anchored graph from requirement to API symbol, test case and test outcome, and seals an evidence package over it. The file holds invocation parameters only. Nothing in it enters a hash, and relative paths resolve against the file.
Nothing in the module build, the tests or the documentation build reads this file.