Safe Data API Test Specification
Annotated ztest sources of the Safe Data API
v0.0-dev+gbb519af
Loading...
Searching...
No Matches
main.c
1/*
2 * Copyright (c) 2026 The safety-toolbox contributors
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Fault-reaction and misuse tests for the Safe Data API. Each scenario in
6 * testcase.yaml selects one CONFIG_SAFE_DATA_FAULT_* reaction. These tests
7 * cause an unrecoverable fault (a kernel panic in one scenario) or an
8 * ISR-context call on purpose, so they live outside the main test suite.
9 */
10
11#include <zephyr/ztest.h>
12#include <zephyr/ztest_error_hook.h>
13#include <zephyr/irq_offload.h>
14#include <string.h>
15
16#include <safe_data/safe_data.h>
17
18struct payload {
19 uint32_t a;
20 uint32_t b;
21};
22
23SAFE_CONTAINER_DEFINE(safe_payload, struct payload);
24
25static struct safe_payload sp;
26
27static void seal(uint32_t a, uint32_t b)
28{
29 struct payload v = { .a = a, .b = b };
30
31 zassert_ok(SAFE_INIT(&sp));
32 zassert_ok(SAFE_WRITE(&sp, &v));
33}
34
35/* ----------------------------------------------------------------------- *
36 * Assertion recorder. CONFIG_ASSERT_TEST lets the post action return, so a
37 * test can see which assertion fired first and in which context. Any other
38 * test checks that no assertion fired at all.
39 * ----------------------------------------------------------------------- */
40static volatile int assert_count;
41static const char *volatile assert_file;
42static volatile bool assert_in_isr;
43
44#ifdef CONFIG_ASSERT_NO_FILE_INFO
45void assert_post_action(void)
46{
47 const char *file = NULL;
48#else
49void assert_post_action(const char *file, unsigned int line)
50{
51 ARG_UNUSED(line);
52#endif
53 if (assert_count++ == 0) {
54 assert_file = file;
55 assert_in_isr = k_is_in_isr();
56 }
57}
58
59/* ----------------------------------------------------------------------- *
60 * Reaction HANDLER: this strong definition replaces the library's weak
61 * safe_data_fault_handler().
62 * ----------------------------------------------------------------------- */
63static atomic_t handler_calls;
64static const void *volatile handler_payload;
65static volatile size_t handler_len;
66
67void safe_data_fault_handler(const void *payload, size_t len)
68{
69 handler_payload = payload;
70 handler_len = len;
71 atomic_inc(&handler_calls);
72}
73
74#if defined(CONFIG_SAFE_DATA_FAULT_HANDLER)
75static atomic_t cb_unrecoverable;
76
77static void fault_cb(const struct safe_data_fault_info *info)
78{
80 atomic_inc(&cb_unrecoverable);
81 }
82}
83#endif
84
85/* ----------------------------------------------------------------------- *
86 * Reaction PANIC: the fault happens in a helper thread. The ztest fatal
87 * hook accepts one expected panic from that thread; the kernel then aborts
88 * the thread, and the test thread joins it.
89 * ----------------------------------------------------------------------- */
90#if defined(CONFIG_SAFE_DATA_FAULT_PANIC)
91static volatile bool panic_caught;
92static volatile bool returned_after_fault;
93
94void ztest_post_fatal_error_hook(unsigned int reason,
95 const struct arch_esf *esf)
96{
97 ARG_UNUSED(esf);
98 panic_caught = (reason == K_ERR_KERNEL_PANIC);
99}
100
101static K_THREAD_STACK_DEFINE(fault_stack, 2048);
102static struct k_thread fault_thread;
103
104static void fault_fn(void *a, void *b, void *c)
105{
106 ARG_UNUSED(a); ARG_UNUSED(b); ARG_UNUSED(c);
107
108 ztest_set_fault_valid(true);
109 (void)SAFE_VERIFY(&sp);
110 /* Not reached when the reaction panics. */
111 returned_after_fault = true;
112 ztest_set_fault_valid(false);
113}
114#endif
115
130ZTEST(safe_data_fault, test_fault_reaction)
131{
132 atomic_clear(&handler_calls);
133#if defined(CONFIG_SAFE_DATA_FAULT_HANDLER)
134 atomic_clear(&cb_unrecoverable);
135#endif
136 assert_count = 0;
137
138 seal(1, 2);
139 sp.payload.a ^= 0x5A5A5A5Au; /* corrupt behind the API's back */
140
141#if defined(CONFIG_SAFE_DATA_FAULT_PANIC)
142 panic_caught = false;
143 returned_after_fault = false;
144 k_thread_create(&fault_thread, fault_stack,
145 K_THREAD_STACK_SIZEOF(fault_stack), fault_fn,
146 NULL, NULL, NULL, 5, 0, K_NO_WAIT);
147 zassert_ok(k_thread_join(&fault_thread, K_FOREVER));
148 zassert_true(panic_caught, "PANIC: the fault must cause a kernel panic");
149 zassert_false(returned_after_fault,
150 "PANIC: the faulting call must not return");
151 zassert_equal(atomic_get(&handler_calls), 0,
152 "PANIC: the handler must not run");
153#else
154 zassert_equal(SAFE_VERIFY(&sp), -EILSEQ,
155 "the error code must reach the caller");
156#if defined(CONFIG_SAFE_DATA_FAULT_HANDLER)
157 zassert_equal(atomic_get(&handler_calls), 1,
158 "HANDLER: the handler must run once");
159 zassert_equal_ptr(handler_payload, &sp.payload);
160 zassert_equal(handler_len, sizeof(sp.payload));
161
162 /* A registered callback takes precedence over the weak handler. */
164 zassert_equal(SAFE_VERIFY(&sp), -EILSEQ);
165 zassert_equal(atomic_get(&cb_unrecoverable), 1);
166 zassert_equal(atomic_get(&handler_calls), 1,
167 "HANDLER: a registered callback replaces the handler");
168 zassert_ok(safe_data_fault_handler_register(NULL));
169#else
170 zassert_equal(atomic_get(&handler_calls), 0,
171 "RETURN: the handler must not run");
172#endif
173#endif
174 zassert_equal(assert_count, 0, "no assertion expected");
175}
176
177static volatile int isr_ret;
178
179static void isr_read(const void *arg)
180{
181 struct payload out;
182
183 ARG_UNUSED(arg);
184 isr_ret = SAFE_READ(&sp, &out);
185}
186
200ZTEST(safe_data_fault, test_lock_from_isr_asserts)
201{
202 struct payload out;
203
204 seal(3, 4);
205
206 assert_count = 0;
207 zassert_ok(SAFE_READ(&sp, &out));
208 zassert_equal(assert_count, 0, "thread context: no assertion");
209
210 assert_file = NULL;
211 assert_in_isr = false;
212 irq_offload(isr_read, NULL);
213
214 zassert_true(assert_count >= 1, "ISR context: the call must assert");
215 zassert_true(assert_in_isr, "the assertion must fire in the ISR");
216 zassert_not_null(assert_file);
217 zassert_not_null(strstr(assert_file, "safe_data/safe_data.h"),
218 "the first assertion must be the Safe Data ISR check");
219 assert_count = 0;
220}
221
227
232ZTEST_SUITE(safe_data_fault, NULL, NULL, NULL, NULL, NULL);
#define SAFE_READ(_c, _out_ptr)
#define SAFE_INIT(_c)
#define SAFE_VERIFY(_c)
#define SAFE_WRITE(_c, _in_ptr)
void safe_data_fault_handler(const void *payload, size_t len)
int safe_data_fault_handler_register(safe_data_fault_cb_t cb)
SAFE_DATA_FAULT_KIND_UNRECOVERABLE
void test_lock_from_isr_asserts(void)
Locking call from an ISR is caught by an assertion.
Definition main.c:200
void test_fault_reaction(void)
Unrecoverable fault gets the configured reaction.
Definition main.c:130
static safe_data_fault_cb_t fault_cb
Definition main.c:15
enum safe_data_fault_kind kind