1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2022, Linaro Limited
4 */
5
6 #include <assert.h>
7 #include <fault_mitigation.h>
8 #include <initcall.h>
9 #include <kernel/thread.h>
10 #include <trace.h>
11 #include <types_ext.h>
12
13 /*
14 * Simple straightforward tests.
15 */
16 static TEE_Result simple_call_func_res;
17
simple_call_func1(void)18 static TEE_Result __noinline simple_call_func1(void)
19 {
20 TEE_Result res = simple_call_func_res;
21
22 FTMN_CALLEE_DONE(res);
23 return res;
24 }
25
simple_call_memcmp(const void * s1,const void * s2,size_t n)26 static TEE_Result __noinline simple_call_memcmp(const void *s1, const void *s2,
27 size_t n)
28 {
29 if (!FTMN_CALLEE_DONE_MEMCMP(memcmp, s1, s2, n))
30 return TEE_SUCCESS;
31 return TEE_ERROR_GENERIC;
32 }
33
simple_call(void)34 static void __noinline simple_call(void)
35 {
36 TEE_Result res = TEE_SUCCESS;
37 struct ftmn ftmn = { };
38 static const char s1[] = "s1";
39
40 simple_call_func_res = TEE_SUCCESS;
41 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0, simple_call_func1);
42 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(1), res);
43
44 simple_call_func_res = TEE_ERROR_GENERIC;
45 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0, simple_call_func1);
46 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(2, 1), res);
47
48 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0,
49 simple_call_memcmp, s1, s1, sizeof(s1));
50 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(3, 2), res);
51 }
52
53 /*
54 * Simulate calling with multiple unmitigated functions in the chain
55 * between checked callee and the caller. The result has always been set
56 * regardless of return value.
57 */
58
two_level_call_memcmp2(const void * s1,const void * s2,size_t n)59 static TEE_Result __noinline two_level_call_memcmp2(const void *s1,
60 const void *s2, size_t n)
61 {
62 if (!FTMN_CALLEE_DONE_MEMCMP(memcmp, s1, s2, n))
63 return TEE_SUCCESS;
64 /*
65 * If FTMN_CALLEE_DONE_MEMCMP() returned non-zero the strings are
66 * different. Update with an error code we can understand.
67 */
68 FTMN_CALLEE_UPDATE_NOT_ZERO(TEE_ERROR_GENERIC);
69 return TEE_ERROR_GENERIC;
70 }
71
two_level_call_memcmp1(const void * s1,const void * s2,size_t n)72 static TEE_Result __noinline two_level_call_memcmp1(const void *s1,
73 const void *s2, size_t n)
74 {
75 return two_level_call_memcmp2(s1, s2, n);
76 }
77
two_level_call_memcmp(const void * s1,const void * s2,size_t n)78 static TEE_Result __noinline two_level_call_memcmp(const void *s1,
79 const void *s2, size_t n)
80 {
81 unsigned long func_hash = FTMN_FUNC_HASH("two_level_call_memcmp2");
82 struct ftmn ftmn = { };
83 TEE_Result res = TEE_SUCCESS;
84
85 FTMN_PUSH_LINKED_CALL(&ftmn, func_hash);
86 res = two_level_call_memcmp1(s1, s2, n);
87 FTMN_SET_CHECK_RES_FROM_CALL(&ftmn, 0, res);
88 FTMN_POP_LINKED_CALL(&ftmn);
89 FTMN_CALLEE_DONE_CHECK(&ftmn, FTMN_INCR1, 0, res);
90
91 return res;
92 }
93
two_level_call(void)94 static void __noinline two_level_call(void)
95 {
96 struct ftmn ftmn = { };
97 TEE_Result res = TEE_SUCCESS;
98 static const char s1[] = "s1";
99 static const char s2[] = "s2";
100
101 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0,
102 two_level_call_memcmp, s1, s1, sizeof(s1));
103 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(1), res);
104
105 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0,
106 two_level_call_memcmp, s1, s2, sizeof(s1));
107 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(2, 1), res);
108 }
109
110 /*
111 * Simulate chained calls in several levels.
112 *
113 * For instance ree_fs_ta_open() -> shdr_verify_signature() ->
114 * crypto_acipher_rsassa_verify() -> ... ->
115 * mbedtls_rsa_rsassa_pss_verify_ext()
116 */
117
chained_call_memcmp2(const void * s1,const void * s2,size_t n)118 static TEE_Result __noinline chained_call_memcmp2(const void *s1,
119 const void *s2, size_t n)
120 {
121 if (!FTMN_CALLEE_DONE_MEMCMP(memcmp, s1, s2, n))
122 return TEE_SUCCESS;
123 return TEE_ERROR_GENERIC;
124 }
125
chained_call_memcmp1(const void * s1,const void * s2,size_t n)126 static TEE_Result __noinline chained_call_memcmp1(const void *s1,
127 const void *s2, size_t n)
128 {
129 TEE_Result res = chained_call_memcmp2(s1, s2, n);
130
131 /*
132 * If s1 and s2 has the same content but different pointers we're
133 * testing the case with an error detected after the linked leaf
134 * function has been called.
135 */
136 if (!res && s1 != s2)
137 res = TEE_ERROR_BAD_STATE;
138
139 return res;
140 }
141
chained_call_memcmp(const void * s1,const void * s2,size_t n)142 static TEE_Result __noinline chained_call_memcmp(const void *s1,
143 const void *s2, size_t n)
144 {
145 struct ftmn ftmn = { };
146 TEE_Result res = TEE_SUCCESS;
147
148 FTMN_PUSH_LINKED_CALL(&ftmn, FTMN_FUNC_HASH("chained_call_memcmp2"));
149
150 res = chained_call_memcmp1(s1, s2, n);
151
152 if (!res)
153 FTMN_SET_CHECK_RES_FROM_CALL(&ftmn, FTMN_INCR0, res);
154 else
155 FTMN_SET_CHECK_RES(&ftmn, FTMN_INCR0, res);
156 FTMN_POP_LINKED_CALL(&ftmn);
157 FTMN_CALLEE_DONE_CHECK(&ftmn, FTMN_INCR0, FTMN_STEP_COUNT(1), res);
158
159 return res;
160 }
161
chained_calls(void)162 static void __noinline chained_calls(void)
163 {
164 struct ftmn ftmn = { };
165 static const char s[] = "s1s2s1";
166 TEE_Result res = TEE_SUCCESS;
167
168 /* Test a normal success case. */
169 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0, chained_call_memcmp, s, s, 2);
170 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(1), res);
171
172 /* Test the case where the leaf function detects an error. */
173 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0,
174 chained_call_memcmp, s, s + 2, 2);
175 assert(res == TEE_ERROR_GENERIC);
176 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(2, 1),
177 TEE_ERROR_GENERIC);
178
179 /*
180 * Test the case where a function in the call chain detects an error
181 * after a the leaf function has returned success.
182 */
183 FTMN_CALL_FUNC(res, &ftmn, FTMN_INCR0,
184 chained_call_memcmp, s, s + 4, 2);
185 assert(res == TEE_ERROR_BAD_STATE);
186 ftmn_expect_state(&ftmn, FTMN_INCR1, FTMN_STEP_COUNT(3, 2),
187 TEE_ERROR_BAD_STATE);
188 }
189
190 #define CALL_TEST_FUNC(x) do { \
191 DMSG("Calling " #x "()"); \
192 x(); \
193 DMSG("Return from " #x "()"); \
194 } while (0)
195
ftmn_boot_tests(void)196 static TEE_Result ftmn_boot_tests(void)
197 {
198 CALL_TEST_FUNC(simple_call);
199 CALL_TEST_FUNC(two_level_call);
200 CALL_TEST_FUNC(chained_calls);
201
202 DMSG("*************************************************");
203 DMSG("************** Tests complete *****************");
204 DMSG("*************************************************");
205 return TEE_SUCCESS;
206 }
207
208 driver_init_late(ftmn_boot_tests);
209