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