Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Documentation/rv: Add documentation for linear temporal logic monitors

Add documents describing linear temporal logic runtime verification
monitors and how to generate them using rvgen.

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Gabriele Monaco <gmonaco@redhat.com>
Link: https://lore.kernel.org/be13719e66fd8da147d7c69d5365aa23c52b743f.1751634289.git.namcao@linutronix.de
Signed-off-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Nam Cao and committed by
Steven Rostedt (Google)
e93648e8 97ffa4ce

+274 -16
+1
Documentation/trace/rv/index.rst
··· 8 8 9 9 runtime-verification.rst 10 10 deterministic_automata.rst 11 + linear_temporal_logic.rst 11 12 monitor_synthesis.rst 12 13 da_monitor_instrumentation.rst 13 14 monitor_wip.rst
+133
Documentation/trace/rv/linear_temporal_logic.rst
··· 1 + Linear temporal logic 2 + ===================== 3 + 4 + Introduction 5 + ------------ 6 + 7 + Runtime verification monitor is a verification technique which checks that the 8 + kernel follows a specification. It does so by using tracepoints to monitor the 9 + kernel's execution trace, and verifying that the execution trace sastifies the 10 + specification. 11 + 12 + Initially, the specification can only be written in the form of deterministic 13 + automaton (DA). However, while attempting to implement DA monitors for some 14 + complex specifications, deterministic automaton is found to be inappropriate as 15 + the specification language. The automaton is complicated, hard to understand, 16 + and error-prone. 17 + 18 + Thus, RV monitors based on linear temporal logic (LTL) are introduced. This type 19 + of monitor uses LTL as specification instead of DA. For some cases, writing the 20 + specification as LTL is more concise and intuitive. 21 + 22 + Many materials explain LTL in details. One book is:: 23 + 24 + Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT 25 + Press, 2008. 26 + 27 + Grammar 28 + ------- 29 + 30 + Unlike some existing syntax, kernel's implementation of LTL is more verbose. 31 + This is motivated by considering that the people who read the LTL specifications 32 + may not be well-versed in LTL. 33 + 34 + Grammar: 35 + ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl 36 + 37 + Operands (opd): 38 + true, false, user-defined names consisting of upper-case characters, digits, 39 + and underscore. 40 + 41 + Unary Operators (unop): 42 + always 43 + eventually 44 + not 45 + 46 + Binary Operators (binop): 47 + until 48 + and 49 + or 50 + imply 51 + equivalent 52 + 53 + This grammar is ambiguous: operator precedence is not defined. Parentheses must 54 + be used. 55 + 56 + Example linear temporal logic 57 + ----------------------------- 58 + .. code-block:: 59 + 60 + RAIN imply (GO_OUTSIDE imply HAVE_UMBRELLA) 61 + 62 + means: if it is raining, going outside means having an umbrella. 63 + 64 + .. code-block:: 65 + 66 + RAIN imply (WET until not RAIN) 67 + 68 + means: if it is raining, it is going to be wet until the rain stops. 69 + 70 + .. code-block:: 71 + 72 + RAIN imply eventually not RAIN 73 + 74 + means: if it is raining, rain will eventually stop. 75 + 76 + The above examples are referring to the current time instance only. For kernel 77 + verification, the `always` operator is usually desirable, to specify that 78 + something is always true at the present and for all future. For example:: 79 + 80 + always (RAIN imply eventually not RAIN) 81 + 82 + means: *all* rain eventually stops. 83 + 84 + In the above examples, `RAIN`, `GO_OUTSIDE`, `HAVE_UMBRELLA` and `WET` are the 85 + "atomic propositions". 86 + 87 + Monitor synthesis 88 + ----------------- 89 + 90 + To synthesize an LTL into a kernel monitor, the `rvgen` tool can be used: 91 + `tools/verification/rvgen`. The specification needs to be provided as a file, 92 + and it must have a "RULE = LTL" assignment. For example:: 93 + 94 + RULE = always (ACQUIRE imply ((not KILLED and not CRASHED) until RELEASE)) 95 + 96 + which says: if `ACQUIRE`, then `RELEASE` must happen before `KILLED` or 97 + `CRASHED`. 98 + 99 + The LTL can be broken down using sub-expressions. The above is equivalent to: 100 + 101 + .. code-block:: 102 + 103 + RULE = always (ACQUIRE imply (ALIVE until RELEASE)) 104 + ALIVE = not KILLED and not CRASHED 105 + 106 + From this specification, `rvgen` generates the C implementation of a Buchi 107 + automaton - a non-deterministic state machine which checks the satisfiability of 108 + the LTL. See Documentation/trace/rv/monitor_synthesis.rst for details on using 109 + `rvgen`. 110 + 111 + References 112 + ---------- 113 + 114 + One book covering model checking and linear temporal logic is:: 115 + 116 + Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, The MIT 117 + Press, 2008. 118 + 119 + For an example of using linear temporal logic in software testing, see:: 120 + 121 + Ruijie Meng, Zhen Dong, Jialin Li, Ivan Beschastnikh, and Abhik Roychoudhury. 122 + 2022. Linear-time temporal logic guided greybox fuzzing. In Proceedings of the 123 + 44th International Conference on Software Engineering (ICSE '22). Association 124 + for Computing Machinery, New York, NY, USA, 1343–1355. 125 + https://doi.org/10.1145/3510003.3510082 126 + 127 + The kernel's LTL monitor implementation is based on:: 128 + 129 + Gerth, R., Peled, D., Vardi, M.Y., Wolper, P. (1996). Simple On-the-fly 130 + Automatic Verification of Linear Temporal Logic. In: Dembiński, P., Średniawa, 131 + M. (eds) Protocol Specification, Testing and Verification XV. PSTV 1995. IFIP 132 + Advances in Information and Communication Technology. Springer, Boston, MA. 133 + https://doi.org/10.1007/978-0-387-34892-6_1
+140 -16
Documentation/trace/rv/monitor_synthesis.rst
··· 39 39 RV monitor synthesis 40 40 -------------------- 41 41 42 - The synthesis of automata-based models into the Linux *RV monitor* abstraction 43 - is automated by the rvgen tool and the rv/da_monitor.h header file that 44 - contains a set of macros that automatically generate the monitor's code. 42 + The synthesis of a specification into the Linux *RV monitor* abstraction is 43 + automated by the rvgen tool and the header file containing common code for 44 + creating monitors. The header files are: 45 + 46 + * rv/da_monitor.h for deterministic automaton monitor. 47 + * rv/ltl_monitor.h for linear temporal logic monitor. 45 48 46 49 rvgen 47 50 ----- 48 51 49 - The rvgen utility leverages dot2c by converting an automaton model in 50 - the DOT format into the C representation [1] and creating the skeleton of 51 - a kernel monitor in C. 52 + The rvgen utility converts a specification into the C presentation and creating 53 + the skeleton of a kernel monitor in C. 52 54 53 55 For example, it is possible to transform the wip.dot model present in 54 56 [1] into a per-cpu monitor with the following command:: ··· 65 63 The wip.c file contains the monitor declaration and the starting point for 66 64 the system instrumentation. 67 65 68 - Monitor macros 69 - -------------- 66 + Similarly, a linear temporal logic monitor can be generated with the following 67 + command:: 70 68 71 - The rv/da_monitor.h enables automatic code generation for the *Monitor 72 - Instance(s)* using C macros. 69 + $ rvgen monitor -c ltl -s pagefault.ltl -t per_task 73 70 74 - The benefits of the usage of macro for monitor synthesis are 3-fold as it: 71 + This generates pagefault/ directory with: 75 72 76 - - Reduces the code duplication; 77 - - Facilitates the bug fix/improvement; 78 - - Avoids the case of developers changing the core of the monitor code 79 - to manipulate the model in a (let's say) non-standard way. 73 + - pagefault.h: The Buchi automaton (the non-deterministic state machine to 74 + verify the specification) 75 + - pagefault.c: The skeleton for the RV monitor 76 + 77 + Monitor header files 78 + -------------------- 79 + 80 + The header files: 81 + 82 + - `rv/da_monitor.h` for deterministic automaton monitor 83 + - `rv/ltl_monitor` for linear temporal logic monitor 84 + 85 + include common macros and static functions for implementing *Monitor 86 + Instance(s)*. 87 + 88 + The benefits of having all common functionalities in a single header file are 89 + 3-fold: 90 + 91 + - Reduce the code duplication; 92 + - Facilitate the bug fix/improvement; 93 + - Avoid the case of developers changing the core of the monitor code to 94 + manipulate the model in a (let's say) non-standard way. 95 + 96 + rv/da_monitor.h 97 + +++++++++++++++ 80 98 81 99 This initial implementation presents three different types of monitor instances: 82 100 ··· 152 130 To notify the monitor that the system will be returning to the initial state, 153 131 so the system and the monitor should be in sync. 154 132 133 + rv/ltl_monitor.h 134 + ++++++++++++++++ 135 + This file must be combined with the $(MODEL_NAME).h file (generated by `rvgen`) 136 + to be complete. For example, for the `pagefault` monitor, the `pagefault.c` 137 + source file must include:: 138 + 139 + #include "pagefault.h" 140 + #include <rv/ltl_monitor.h> 141 + 142 + (the skeleton monitor file generated by `rvgen` already does this). 143 + 144 + `$(MODEL_NAME).h` (`pagefault.h` in the above example) includes the 145 + implementation of the Buchi automaton - a non-deterministic state machine that 146 + verifies the LTL specification. While `rv/ltl_monitor.h` includes the common 147 + helper functions to interact with the Buchi automaton and to implement an RV 148 + monitor. An important definition in `$(MODEL_NAME).h` is:: 149 + 150 + enum ltl_atom { 151 + LTL_$(FIRST_ATOMIC_PROPOSITION), 152 + LTL_$(SECOND_ATOMIC_PROPOSITION), 153 + ... 154 + LTL_NUM_ATOM 155 + }; 156 + 157 + which is the list of atomic propositions present in the LTL specification 158 + (prefixed with "LTL\_" to avoid name collision). This `enum` is passed to the 159 + functions interacting with the Buchi automaton. 160 + 161 + While generating code, `rvgen` cannot understand the meaning of the atomic 162 + propositions. Thus, that task is left for manual work. The recommended pratice 163 + is adding tracepoints to places where the atomic propositions change; and in the 164 + tracepoints' handlers: the Buchi automaton is executed using:: 165 + 166 + void ltl_atom_update(struct task_struct *task, enum ltl_atom atom, bool value) 167 + 168 + which tells the Buchi automaton that the atomic proposition `atom` is now 169 + `value`. The Buchi automaton checks whether the LTL specification is still 170 + satisfied, and invokes the monitor's error tracepoint and the reactor if 171 + violation is detected. 172 + 173 + Tracepoints and `ltl_atom_update()` should be used whenever possible. However, 174 + it is sometimes not the most convenient. For some atomic propositions which are 175 + changed in multiple places in the kernel, it is cumbersome to trace all those 176 + places. Furthermore, it may not be important that the atomic propositions are 177 + updated at precise times. For example, considering the following linear temporal 178 + logic:: 179 + 180 + RULE = always (RT imply not PAGEFAULT) 181 + 182 + This LTL states that a real-time task does not raise page faults. For this 183 + specification, it is not important when `RT` changes, as long as it has the 184 + correct value when `PAGEFAULT` is true. Motivated by this case, another 185 + function is introduced:: 186 + 187 + void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon) 188 + 189 + This function is called whenever the Buchi automaton is triggered. Therefore, it 190 + can be manually implemented to "fetch" `RT`:: 191 + 192 + void ltl_atom_fetch(struct task_struct *task, struct ltl_monitor *mon) 193 + { 194 + ltl_atom_set(mon, LTL_RT, rt_task(task)); 195 + } 196 + 197 + Effectively, whenever `PAGEFAULT` is updated with a call to `ltl_atom_update()`, 198 + `RT` is also fetched. Thus, the LTL specification can be verified without 199 + tracing `RT` everywhere. 200 + 201 + For atomic propositions which act like events, they usually need to be set (or 202 + cleared) and then immediately cleared (or set). A convenient function is 203 + provided:: 204 + 205 + void ltl_atom_pulse(struct task_struct *task, enum ltl_atom atom, bool value) 206 + 207 + which is equivalent to:: 208 + 209 + ltl_atom_update(task, atom, value); 210 + ltl_atom_update(task, atom, !value); 211 + 212 + To initialize the atomic propositions, the following function must be 213 + implemented:: 214 + 215 + ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) 216 + 217 + This function is called for all running tasks when the monitor is enabled. It is 218 + also called for new tasks created after the enabling the monitor. It should 219 + initialize as many atomic propositions as possible, for example:: 220 + 221 + void ltl_atom_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation) 222 + { 223 + ltl_atom_set(mon, LTL_RT, rt_task(task)); 224 + if (task_creation) 225 + ltl_atom_set(mon, LTL_PAGEFAULT, false); 226 + } 227 + 228 + Atomic propositions not initialized by `ltl_atom_init()` will stay in the 229 + unknown state until relevant tracepoints are hit, which can take some time. As 230 + monitoring for a task cannot be done until all atomic propositions is known for 231 + the task, the monitor may need some time to start validating tasks which have 232 + been running before the monitor is enabled. Therefore, it is recommended to 233 + start the tasks of interest after enabling the monitor. 234 + 155 235 Final remarks 156 236 ------------- 157 237 158 - With the monitor synthesis in place using the rv/da_monitor.h and 238 + With the monitor synthesis in place using the header files and 159 239 rvgen, the developer's work should be limited to the instrumentation 160 240 of the system, increasing the confidence in the overall approach. 161 241