Software APIs
cfi.h
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CFI_H_
6 #define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CFI_H_
7 
9 
10 /**
11  * @brief Control Flow Integrity (CFI).
12  *
13  * The CFI_FUNC_COUNTER macros provide utilities to implement forward-edge and
14  * block level control flow checks in software. The checks are based on
15  * per-function counters that are monotonically incremented when the current
16  * value matches the provided static expected value.
17  *
18  * To integrate CFI_FUNC_COUNTER checks into a module, first define a table
19  * enumerating all the functions that need to be included in control flow
20  * checks along with counter initial values, for example:
21  *
22  * ```
23  * #define CFI_FUNC_COUNTERS_TABLE(X) \
24  * X(kCfiFunc1, 0x705) \
25  * X(kCfiFunc2, 0x042) \
26  * X(kCfiFunc3, 0x29d) \
27  * ```
28  *
29  * It is recommended to use 11-bit initial values to enable the use of load
30  * immediate instructions in the generated assembly.
31  *
32  * Use the `CFI_DEFINE_COUNTERS()` macro to initialize the counter table as well
33  * as constant values required by the CFI framework. The following example uses
34  * the table definition from the previous step.
35  *
36  * ```
37  * CFI_DEFINE_COUNTERS(counters_table, CFI_FUNC_COUNTERS_TABLE);
38  * ```
39  *
40  * Forward-edge checks:
41  *
42  * In the following example, func1 initializes the fnc2 counter before calling
43  * the function using the `CFI_FUNC_COUNTER_PREPCALL()` macro. Both function
44  * counters are checked after fnc2 returns, which gives a high level of
45  * confidence that the control flow executed as expected.
46  *
47  * ```
48  * void func2(void) {
49  * CFI_FUNC_COUNTER_INCREMENT(counters_table, kCfiFunc2, 1);
50  * ...
51  * CFI_FUNC_COUNTER_INCREMENT(counters_table, kCfiFunc2, 2);
52  * ...
53  * CFI_FUNC_COUNTER_INCREMENT(counters_table, kCfiFunc2, 3);
54  * }
55  *
56  * void func1(void) {
57  * CFI_FUNC_COUNTER_INIT(counters_table, kCfiFunc1);
58  *
59  * CFI_FUNC_COUNTER_PREPCALL(counters_table, kCfiFunc1, 1, kCfiFunc2);
60  * func2();
61  * CFI_FUNC_COUNTER_INCREMENT(counters_table, kCfiFunc1, 3);
62  * CFI_FUNC_COUNTER_CHECK(counters_table, kCfiFunc2, 4);
63  * }
64  * ```
65  *
66  * The implementation is based on https://hal.inria.fr/hal-01059201, with the
67  * exception that all counters are expected to have good hamming distance
68  * initialization and increment values to defend against potential faults. This
69  * delta comes at a low cost given that all comparisons are performed against
70  * constant values.
71  */
72 
73 /**
74  * Defines all counter initialization constants. Used inside
75  * `CFI_DEFINE_COUNTERS()`.
76  */
77 #define CFI_FUNC_COUNTER_INIT_CONSTANTS_(name_, value_) name_##Val0 = value_,
78 
79 /**
80  * Initializes all counter values to zero. Used inside `CFI_DEFINE_COUNTERS()`.
81  */
82 #define CFI_FUNC_COUNTERS_TABLE_INIT_(name_, value_) 0,
83 
84 /**
85  * Defines counter indexes. Used inside `CFI_DEFINE_COUNTERS()`.
86  */
87 #define CFI_FUNC_COUNTER_INDEXES_(name_, value_) name_,
88 
89 /**
90  * Defines the counters table as well as constants required by other CFI counter
91  * macros.
92  *
93  * @param table_name_ Name of the array variable used to store all the counters.
94  * @param table_ Macro enumerating all the function identifiers and their
95  * respective initial values.
96  */
97 #define CFI_DEFINE_COUNTERS(table_name_, table_) \
98  enum { table_(CFI_FUNC_COUNTER_INIT_CONSTANTS_) }; \
99  enum { table_(CFI_FUNC_COUNTER_INDEXES_) }; \
100  uint32_t table_name_[] = {table_(CFI_FUNC_COUNTERS_TABLE_INIT_)}
101 
102 enum {
103  // Counter increment constant used in counter initialization and increment
104  // operations.
105  kCfiIncrement = 0x5a,
106 };
107 
108 /**
109  * Initializes the CFI counter at `index` with its respective initialization
110  * constant plus `kCfiIncrement`.
111  *
112  * This macro calls `barrier32()` to synchronize the counter update.
113  *
114  * @param table The counters array variable.
115  * @param index The counter index.
116  */
117 #define CFI_FUNC_COUNTER_INIT(table, index) \
118  do { \
119  table[index] = (index##Val0 + kCfiIncrement); \
120  barrier32(table[index]); \
121  } while (0)
122 
123 /**
124  * Converts a CFI step value into an expected counter value.
125  *
126  * @param index The counter index.
127  * @param step The increment step.
128  */
129 #define CFI_STEP_TO_COUNT(index, step) ((index##Val0) + (step)*kCfiIncrement)
130 
131 /**
132  * Increments the CFI counter at `index` if the current count is equivalent to
133  * the provided `step`. It throws an irrecoverable exception otherwise.
134  *
135  * This macro calls `barrier32()` to synchronize the counter update.
136  *
137  * @param table The counters array variable.
138  * @param index The counter index.
139  * @param step The equivalent step for the current counter value.
140  */
141 #define CFI_FUNC_COUNTER_INCREMENT(table, index, step) \
142  do { \
143  HARDENED_CHECK_EQ(table[index], CFI_STEP_TO_COUNT(index, step)); \
144  table[index] += kCfiIncrement; \
145  barrier32(table[index]); \
146  } while (0)
147 
148 /**
149  * Prepare counters for function call.
150  *
151  * The `src` and `target` counters are associated with the caller and the
152  * callee functions, respectively. The caller uses this macro to initialize
153  * the `target` counter between increments of the `src` counter.
154  *
155  * The `src` counter can be verified at a later time to get a high confidence
156  * measurement that the target counter was initialized properly by the caller
157  * before entering the callee function.
158  *
159  * This macro uses `CFI_FUNC_COUNTER_INCREMENT()` and `CFI_FUNC_COUNTER_INIT()`
160  * which use `barrier32()` to synchronize all counter updates. An irrecoverable
161  * exception is thrown if an unexpected `src` count value is found.
162  *
163  * @param table The counters array variable.
164  * @param src Index counter associated with the caller function.
165  * @param src_step Initial expected step of the `src` counter.
166  * @param target Index counter associated with the calee function.
167  */
168 #define CFI_FUNC_COUNTER_PREPCALL(table, src, src_step, target) \
169  do { \
170  CFI_FUNC_COUNTER_INCREMENT(table, src, src_step); \
171  CFI_FUNC_COUNTER_INIT(table, target); \
172  CFI_FUNC_COUNTER_INCREMENT(table, src, (src_step + 1)); \
173  } while (0)
174 
175 /**
176  * Compares the equivalent counter value of the counter at `index` against the
177  * provided `step` value. Throws an irrecoverable exception on mismatch.
178  *
179  * @param table The counters array variable.
180  * @param index The counter index.
181  * @param step The equivalent step for the counter value.
182  */
183 #define CFI_FUNC_COUNTER_CHECK(table, index, step) \
184  do { \
185  HARDENED_CHECK_EQ(table[index], CFI_STEP_TO_COUNT(index, step)); \
186  } while (0)
187 
188 #endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_CFI_H_