1 /*
2 * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #ifndef __TFM_FIH_H__
9 #define __TFM_FIH_H__
10
11 #include <stddef.h>
12 #include <stdint.h>
13
14 /*
15 * Fault injection mitigation library.
16 *
17 * Has support for different measures, which can either be enabled/disabled
18 * separately or by defining one of the TFM_FIH_PROFILEs.
19 *
20 * NOTE: It is not guaranteed that these constructs against fault injection
21 * attacks can be preserved in all compilers.
22 *
23 * FIH_ENABLE_DOUBLE_VARS makes critical variables into a tuple (x, x ^ msk).
24 * Then the correctness of x can be checked by XORing the two tuple values
25 * together. This also means that comparisons between fih_ints can be verified
26 * by doing x == y && x_msk == y_msk.
27 *
28 * FIH_ENABLE_GLOBAL_FAIL makes all while(1) failure loops redirect to a global
29 * failure loop. This loop has mitigations against loop escapes / unlooping.
30 * This also means that any unlooping won't immediately continue executing the
31 * function that was executing before the failure.
32 *
33 * FIH_ENABLE_CFI (Control Flow Integrity) creates a global counter that is
34 * incremented before every FIH_CALL of vulnerable functions. On the function
35 * return the counter is decremented, and after the return it is verified that
36 * the counter has the same value as before this process. This can be used to
37 * verify that the function has actually been called. This protection is
38 * intended to discover that important functions are called in an expected
39 * sequence and none of them is missed due to an instruction skip which could
40 * be a result of glitching attack. It does not provide protection against ROP
41 * or JOP attacks.
42 *
43 * FIH_ENABLE_DELAY causes random delays. This makes it hard to cause faults
44 * precisely. It requires an RNG. A simple example using SysTick as entropy
45 * source is provided in tfm_fih_rng.h, but any RNG that has an entropy
46 * source can be used by implementing the fih_delay_random function.
47 *
48 * The basic call pattern is:
49 *
50 * fih_int fih_rc = FIH_FAILURE;
51 * FIH_CALL(vulnerable_function, fih_rc, arg1, arg2);
52 * if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
53 * error_handling();
54 * }
55 *
56 * If a fault injection is detected, call FIH_PANIC to trap the execution.
57 *
58 * Note that any function called by FIH_CALL must only return using FIH_RET,
59 * as otherwise the CFI counter will not be decremented and the CFI check will
60 * fail causing a panic.
61 */
62
63 #ifdef __cplusplus
64 extern "C" {
65 #endif /* __cplusplus */
66
67 #undef FIH_ENABLE_GLOBAL_FAIL
68 #undef FIH_ENABLE_CFI
69 #undef FIH_ENABLE_DOUBLE_VARS
70 #undef FIH_ENABLE_DELAY
71
72 #ifdef TFM_FIH_PROFILE_ON
73 #if defined(TFM_FIH_PROFILE_LOW)
74 #define FIH_ENABLE_GLOBAL_FAIL
75 #define FIH_ENABLE_CFI
76
77 #elif defined(TFM_FIH_PROFILE_MEDIUM)
78 #define FIH_ENABLE_DOUBLE_VARS
79 #define FIH_ENABLE_GLOBAL_FAIL
80 #define FIH_ENABLE_CFI
81
82 #elif defined(TFM_FIH_PROFILE_HIGH)
83 #define FIH_ENABLE_DELAY /* Requires an hardware entropy source */
84 #define FIH_ENABLE_DOUBLE_VARS
85 #define FIH_ENABLE_GLOBAL_FAIL
86 #define FIH_ENABLE_CFI
87
88 #else
89 #error "Invalid FIH Profile configuration"
90 #endif /* TFM_FIH_PROFILE */
91
92 #define FIH_TRUE 0xC35A
93 #define FIH_FALSE 0x0
94
95 #ifdef FIH_ENABLE_DOUBLE_VARS
96 #define FIH_POSITIVE_VALUE 0x5555AAAA
97 #define FIH_NEGATIVE_VALUE 0xAAAA5555
98
99 /*
100 * A volatile mask is used to prevent compiler optimization - the mask is xored
101 * with the variable to create the backup and the integrity can be checked with
102 * another xor. The mask value doesn't _really_ matter that much, as long as
103 * it has reasonably high hamming weight.
104 */
105 #define _FIH_MASK_VALUE 0xA5C35A3C
106
107 /*
108 * All ints are replaced with two int - the normal one and a backup which is
109 * XORed with the mask.
110 */
111 typedef volatile struct {
112 volatile int32_t val;
113 volatile int32_t msk;
114 } fih_int;
115
116 #define FIH_INT_INIT(x) {(x), (x) ^ _FIH_MASK_VALUE}
117 #else /* FIH_ENABLE_DOUBLE_VARS */
118 #define FIH_POSITIVE_VALUE 0x0
119 #define FIH_NEGATIVE_VALUE 0xAAAA5555
120
121 typedef volatile int32_t fih_int;
122
123 #define FIH_INT_INIT(x) (x)
124 #endif /* FIH_ENABLE_DOUBLE_VARS */
125
126 extern fih_int FIH_SUCCESS;
127 extern fih_int FIH_FAILURE;
128
129 #ifdef FIH_ENABLE_GLOBAL_FAIL
130 /*
131 * Global failure handler - more resistant to unlooping. noinline and used are
132 * used to prevent optimization.
133 *
134 * NOTE
135 * This failure handler shall be used as FIH specific error handling to capture
136 * FI attacks. Error handling in SPM and SP should be enhanced respectively.
137 */
138 __attribute__((noinline)) __attribute__((used)) void fih_panic_loop(void);
139 #define FIH_PANIC fih_panic_loop()
140 #else /* FIH_ENABLE_GLOBAL_FAIL */
141 #define FIH_PANIC \
142 do { \
143 FIH_LABEL("FAILURE_LOOP"); \
144 while (1) {} \
145 } while (0)
146 #endif /* FIH_ENABLE_GLOBAL_FAIL */
147
148 /*
149 * NOTE
150 * For functions to be inlined outside their compilation unit they have to
151 * have the body in the header file. This is required as function calls are easy
152 * to skip.
153 */
154 #ifdef FIH_ENABLE_DELAY
155 /**
156 * @brief Set up the RNG for use with random delays. Called once at startup.
157 */
158 void fih_delay_init(void);
159
160 /**
161 * Get a random uint8_t value from an RNG seeded with an entropy source.
162 *
163 * NOTE
164 * Do not directly call this function.
165 */
166 uint8_t fih_delay_random(void);
167
168 /* Delaying logic, with randomness from a CSPRNG */
169 __attribute__((always_inline)) inline
fih_delay(void)170 void fih_delay(void)
171 {
172 uint32_t i = 0;
173 volatile uint32_t delay = FIH_NEGATIVE_VALUE;
174 volatile uint32_t counter = 0;
175
176 delay = fih_delay_random();
177
178 if (delay == FIH_NEGATIVE_VALUE) {
179 FIH_PANIC;
180 }
181
182 delay &= 0xFF;
183
184 for (i = 0; i < delay; i++) {
185 counter++;
186 }
187
188 if (counter != delay) {
189 FIH_PANIC;
190 }
191 }
192 #else /* FIH_ENABLE_DELAY */
193 #define fih_delay_init()
194
195 #define fih_delay()
196 #endif /* FIH_ENABLE_DELAY */
197
198 #ifdef FIH_ENABLE_DOUBLE_VARS
199 __attribute__((always_inline)) inline
fih_int_validate(fih_int x)200 void fih_int_validate(fih_int x)
201 {
202 if (x.val != (x.msk ^ _FIH_MASK_VALUE)) {
203 FIH_PANIC;
204 }
205 }
206
207 /* Convert a fih_int to an int. Validate for tampering. */
208 __attribute__((always_inline)) inline
fih_int_decode(fih_int x)209 int32_t fih_int_decode(fih_int x)
210 {
211 fih_int_validate(x);
212 return x.val;
213 }
214
215 /* Convert an int to a fih_int, can be used to encode specific error codes. */
216 __attribute__((always_inline)) inline
fih_int_encode(int32_t x)217 fih_int fih_int_encode(int32_t x)
218 {
219 fih_int ret = {x, x ^ _FIH_MASK_VALUE};
220 return ret;
221 }
222
223 /* Standard equality. If A == B then 1, else 0 */
224 __attribute__((always_inline)) inline
fih_eq(fih_int x,fih_int y)225 int32_t fih_eq(fih_int x, fih_int y)
226 {
227 volatile int32_t rc1 = FIH_FALSE;
228 volatile int32_t rc2 = FIH_FALSE;
229
230 fih_int_validate(x);
231 fih_int_validate(y);
232
233 if (x.val == y.val) {
234 rc1 = FIH_TRUE;
235 }
236
237 fih_delay();
238
239 if (x.msk == y.msk) {
240 rc2 = FIH_TRUE;
241 }
242
243 fih_delay();
244
245 if (rc1 != rc2) {
246 FIH_PANIC;
247 }
248
249 return rc1;
250 }
251
252 __attribute__((always_inline)) inline
fih_not_eq(fih_int x,fih_int y)253 int32_t fih_not_eq(fih_int x, fih_int y)
254 {
255 volatile int32_t rc1 = FIH_FALSE;
256 volatile int32_t rc2 = FIH_FALSE;
257
258 fih_int_validate(x);
259 fih_int_validate(y);
260
261 if (x.val != y.val) {
262 rc1 = FIH_TRUE;
263 }
264
265 fih_delay();
266
267 if (x.msk != y.msk) {
268 rc2 = FIH_TRUE;
269 }
270
271 fih_delay();
272
273 if (rc1 != rc2) {
274 FIH_PANIC;
275 }
276
277 return rc1;
278 }
279 #else /* FIH_ENABLE_DOUBLE_VARS */
280 /* NOOP */
281 #define fih_int_validate(x)
282
283 /* NOOP */
284 #define fih_int_decode(x) (x)
285
286 /* NOOP */
287 #define fih_int_encode(x) (x)
288
289 __attribute__((always_inline)) inline
fih_eq(fih_int x,fih_int y)290 int32_t fih_eq(fih_int x, fih_int y)
291 {
292 volatile int32_t rc = FIH_FALSE;
293
294 if (x == y) {
295 rc = FIH_TRUE;
296 }
297
298 fih_delay();
299
300 if (x != y) {
301 rc = FIH_FALSE;
302 }
303
304 return rc;
305 }
306
307 __attribute__((always_inline)) inline
fih_not_eq(fih_int x,fih_int y)308 int32_t fih_not_eq(fih_int x, fih_int y)
309 {
310 volatile int32_t rc = FIH_FALSE;
311
312 if (x != y) {
313 rc = FIH_TRUE;
314 }
315
316 fih_delay();
317
318 if (x == y) {
319 rc = FIH_FALSE;
320 }
321
322 return rc;
323 }
324 #endif /* FIH_ENABLE_DOUBLE_VARS */
325
326 /*
327 * C has a common return pattern where 0 is a correct value and all others are
328 * errors. This function converts 0 to FIH_SUCCESS and any other number to a
329 * value that is not FIH_SUCCESS
330 */
331 __attribute__((always_inline)) inline
fih_int_encode_zero_equality(int32_t x)332 fih_int fih_int_encode_zero_equality(int32_t x)
333 {
334 if (x) {
335 return FIH_FAILURE;
336 } else {
337 return FIH_SUCCESS;
338 }
339 }
340
341 #ifdef FIH_ENABLE_CFI
342 /* Global Control Flow Integrity counter */
343 extern fih_int _fih_cfi_ctr;
344
345 /*
346 * Increment the CFI counter by input counter and return the value before the
347 * increment.
348 *
349 * NOTE
350 * This function shall not be called directly.
351 */
352 fih_int fih_cfi_get_and_increment(uint8_t cnt);
353
354 /*
355 * Validate that the saved precall value is the same as the value of the global
356 * counter. For this to be the case, a fih_ret must have been called between
357 * these functions being executed. If the values aren't the same then panic.
358 *
359 * NOTE
360 * This function shall not be called directly.
361 */
362 void fih_cfi_validate(fih_int saved);
363
364 /*
365 * Decrement the global CFI counter by one, so that it has the same value as
366 * before the cfi_precall.
367 *
368 * NOTE
369 * This function shall not be called directly.
370 */
371 void fih_cfi_decrement(void);
372
373 /*
374 * Macro wrappers for functions - Even when the functions have zero body this
375 * saves a few bytes on noop functions as it doesn't generate the call/ret
376 *
377 * CFI precall function saves the CFI counter and then increments it - the
378 * postcall then checks if the counter is equal to the saved value. In order for
379 * this to be the case a FIH_RET must have been performed inside the called
380 * function in order to decrement the counter, so the function must have been
381 * called.
382 */
383 #define FIH_CFI_PRECALL_BLOCK \
384 fih_int _fih_cfi_precall_saved_value = fih_cfi_get_and_increment(1)
385
386 #define FIH_CFI_POSTCALL_BLOCK \
387 fih_cfi_validate(_fih_cfi_precall_saved_value)
388
389 #define FIH_CFI_PRERET \
390 fih_cfi_decrement()
391
392 /*
393 * Marcos to support protect the control flow integrity inside a function.
394 *
395 * The FIH_CFI_PRECALL_BLOCK/FIH_CFI_POSTCALL_BLOCK pair mainly protect function
396 * calls from fault injection. Fault injection may attack a function to skip its
397 * critical steps which are not function calls. It is difficult for the caller
398 * to dectect the injection as long as the function successfully returns.
399 *
400 * The following macros can be called in a function to track the critical steps,
401 * especially those which are not function calls.
402 */
403 /*
404 * FIH_CFI_STEP_INIT() saves the CFI counter and increase the CFI counter by the
405 * number of the critical steps. It should be called before execution starts.
406 */
407 #define FIH_CFI_STEP_INIT(x) \
408 fih_int _fih_cfi_step_saved_value = fih_cfi_get_and_increment(x)
409
410 /*
411 * FIH_CFI_STEP_DECREMENT() decrease the CFI counter by one. It can be called
412 * after each critical step execution completes.
413 */
414 #define FIH_CFI_STEP_DECREMENT() \
415 fih_cfi_decrement()
416
417 /*
418 * FIH_CFI_STEP_ERR_RESET() resets the CFI counter to the previous value saved
419 * by FIH_CFI_STEP_INIT(). It shall be called only when a functionality error
420 * occurs and forces the function to exit. It can enable the caller to capture
421 * the functionality error other than being trapped in fault injection error
422 * handling.
423 */
424 #define FIH_CFI_STEP_ERR_RESET() \
425 do { \
426 _fih_cfi_ctr = _fih_cfi_step_saved_value; \
427 fih_int_validate(_fih_cfi_ctr); \
428 } while(0)
429
430 #else /* FIH_ENABLE_CFI */
431 #define FIH_CFI_PRECALL_BLOCK
432 #define FIH_CFI_POSTCALL_BLOCK
433 #define FIH_CFI_PRERET
434
435 #define FIH_CFI_STEP_INIT(x)
436 #define FIH_CFI_STEP_DECREMENT()
437 #define FIH_CFI_STEP_ERR_RESET()
438 #endif /* FIH_ENABLE_CFI */
439
440 /*
441 * Label for interacting with FIH testing tool. Can be parsed from the elf file
442 * after compilation. Does not require debug symbols.
443 */
444 #define FIH_LABEL(str) __asm volatile ("FIH_LABEL_" str "_0_%=:" ::)
445 #define FIH_LABEL_CRITICAL_POINT() FIH_LABEL("FIH_CRITICAL_POINT")
446
447 /*
448 * Main FIH calling macro. return variable is second argument. Does some setup
449 * before and validation afterwards. Inserts labels for use with testing script.
450 *
451 * First perform the precall step - this gets the current value of the CFI
452 * counter and saves it to a local variable, and then increments the counter.
453 *
454 * Then set the return variable to FIH_FAILURE as a base case.
455 *
456 * Then perform the function call. As part of the function FIH_RET must be
457 * called which will decrement the counter.
458 *
459 * The postcall step gets the value of the counter and compares it to the
460 * previously saved value. If this is equal then the function call and all child
461 * function calls were performed.
462 */
463 #define FIH_CALL(f, ret, ...) \
464 do { \
465 FIH_LABEL("FIH_CALL_START_" # f); \
466 FIH_CFI_PRECALL_BLOCK; \
467 ret = FIH_FAILURE; \
468 fih_delay(); \
469 ret = f(__VA_ARGS__); \
470 FIH_CFI_POSTCALL_BLOCK; \
471 fih_int_validate(ret); \
472 FIH_LABEL("FIH_CALL_END"); \
473 } while (0)
474
475 /*
476 * FIH return changes the state of the internal state machine. If you do a
477 * FIH_CALL then you need to do a FIH_RET else the state machine will detect
478 * tampering and panic.
479 */
480 #define FIH_RET(ret) \
481 do { \
482 FIH_CFI_PRERET; \
483 return ret; \
484 } while (0)
485
486 /*
487 * FIH return type macro changes the function return types to fih_int.
488 * All functions that need to be protected by FIH and called via FIH_CALL must
489 * return a fih_int type.
490 */
491 #define FIH_RET_TYPE(type) fih_int
492
493 #else /* TFM_FIH_PROFILE_ON */
494 typedef int32_t fih_int;
495
496 #define FIH_INT_INIT(x) (x)
497
498 #define FIH_SUCCESS 0
499 #define FIH_FAILURE -1
500
501 #define fih_int_validate(x)
502
503 #define fih_int_decode(x) (x)
504
505 #define fih_int_encode(x) (x)
506
507 #define fih_int_encode_zero_equality(x) ((x) == 0 ? 0 : 1)
508
509 #define fih_eq(x, y) ((x) == (y))
510
511 #define fih_not_eq(x, y) ((x) != (y))
512
513 #define fih_delay_init() (0)
514 #define fih_delay()
515
516 #define FIH_CALL(f, ret, ...) \
517 do { \
518 ret = f(__VA_ARGS__); \
519 } while (0)
520
521 #define FIH_RET(ret) \
522 do { \
523 return ret; \
524 } while (0)
525
526 #define FIH_RET_TYPE(type) type
527
528 #define FIH_PANIC do { \
529 while(1) {}; \
530 } while (0)
531
532 #define FIH_CFI_STEP_INIT(x)
533 #define FIH_CFI_STEP_DECREMENT()
534 #define FIH_CFI_STEP_ERR_RESET()
535
536 #define FIH_LABEL_CRITICAL_POINT()
537
538 #endif /* TFM_FIH_PROFILE_ON */
539
540 #ifdef __cplusplus
541 }
542 #endif /* __cplusplus */
543
544 #endif /* __TFM_FIH_H__ */
545