1
- kcov : code coverage for fuzzing
1
+ KCOV : code coverage for fuzzing
2
2
===============================
3
3
4
- kcov exposes kernel code coverage information in a form suitable for coverage-
5
- guided fuzzing (randomized testing) . Coverage data of a running kernel is
6
- exported via the " kcov" debugfs file. Coverage collection is enabled on a task
7
- basis, and thus it can capture precise coverage of a single system call.
4
+ KCOV collects and exposes kernel code coverage information in a form suitable
5
+ for coverage- guided fuzzing. Coverage data of a running kernel is exported via
6
+ the `` kcov `` debugfs file. Coverage collection is enabled on a task basis, and
7
+ thus KCOV can capture precise coverage of a single system call.
8
8
9
- Note that kcov does not aim to collect as much coverage as possible. It aims
10
- to collect more or less stable coverage that is function of syscall inputs.
11
- To achieve this goal it does not collect coverage in soft/hard interrupts
12
- and instrumentation of some inherently non-deterministic parts of kernel is
13
- disabled (e.g. scheduler, locking).
9
+ Note that KCOV does not aim to collect as much coverage as possible. It aims
10
+ to collect more or less stable coverage that is a function of syscall inputs.
11
+ To achieve this goal, it does not collect coverage in soft/hard interrupts
12
+ (unless remove coverage collection is enabled, see below) and from some
13
+ inherently non-deterministic parts of the kernel (e.g. scheduler, locking).
14
14
15
- kcov is also able to collect comparison operands from the instrumented code
16
- (this feature currently requires that the kernel is compiled with clang).
15
+ Besides collecting code coverage, KCOV can also collect comparison operands.
16
+ See the "Comparison operands collection" section for details.
17
+
18
+ Besides collecting coverage data from syscall handlers, KCOV can also collect
19
+ coverage for annotated parts of the kernel executing in background kernel
20
+ tasks or soft interrupts. See the "Remote coverage collection" section for
21
+ details.
17
22
18
23
Prerequisites
19
24
-------------
20
25
21
- Configure the kernel with::
26
+ KCOV relies on compiler instrumentation and requires GCC 6.1.0 or later
27
+ or any Clang version supported by the kernel.
22
28
23
- CONFIG_KCOV=y
29
+ Collecting comparison operands is supported with GCC 8+ or with Clang.
24
30
25
- CONFIG_KCOV requires gcc 6.1.0 or later.
31
+ To enable KCOV, configure the kernel with::
26
32
27
- If the comparison operands need to be collected, set::
33
+ CONFIG_KCOV=y
34
+
35
+ To enable comparison operands collection, set::
28
36
29
37
CONFIG_KCOV_ENABLE_COMPARISONS=y
30
38
31
- Profiling data will only become accessible once debugfs has been mounted::
39
+ Coverage data only becomes accessible once debugfs has been mounted::
32
40
33
41
mount -t debugfs none /sys/kernel/debug
34
42
35
43
Coverage collection
36
44
-------------------
37
45
38
- The following program demonstrates coverage collection from within a test
39
- program using kcov :
46
+ The following program demonstrates how to use KCOV to collect coverage for a
47
+ single syscall from within a test program :
40
48
41
49
.. code-block :: c
42
50
@@ -84,7 +92,7 @@ program using kcov:
84
92
perror("ioctl"), exit(1);
85
93
/* Reset coverage from the tail of the ioctl() call. */
86
94
__atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
87
- /* That's the target syscal call. */
95
+ /* Call the target syscall call. */
88
96
read(-1, NULL, 0);
89
97
/* Read number of PCs collected. */
90
98
n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
@@ -103,7 +111,7 @@ program using kcov:
103
111
return 0;
104
112
}
105
113
106
- After piping through addr2line output of the program looks as follows::
114
+ After piping through `` addr2line `` the output of the program looks as follows::
107
115
108
116
SyS_read
109
117
fs/read_write.c:562
@@ -121,12 +129,13 @@ After piping through addr2line output of the program looks as follows::
121
129
fs/read_write.c:562
122
130
123
131
If a program needs to collect coverage from several threads (independently),
124
- it needs to open /sys/kernel/debug/kcov in each thread separately.
132
+ it needs to open `` /sys/kernel/debug/kcov `` in each thread separately.
125
133
126
134
The interface is fine-grained to allow efficient forking of test processes.
127
- That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode,
128
- mmaps coverage buffer and then forks child processes in a loop. Child processes
129
- only need to enable coverage (disable happens automatically on thread end).
135
+ That is, a parent process opens ``/sys/kernel/debug/kcov ``, enables trace mode,
136
+ mmaps coverage buffer, and then forks child processes in a loop. The child
137
+ processes only need to enable coverage (it gets disabled automatically when
138
+ a thread exits).
130
139
131
140
Comparison operands collection
132
141
------------------------------
@@ -205,52 +214,78 @@ Comparison operands collection is similar to coverage collection:
205
214
return 0;
206
215
}
207
216
208
- Note that the kcov modes (coverage collection or comparison operands) are
209
- mutually exclusive.
217
+ Note that the KCOV modes (collection of code coverage or comparison operands)
218
+ are mutually exclusive.
210
219
211
220
Remote coverage collection
212
221
--------------------------
213
222
214
- With KCOV_ENABLE coverage is collected only for syscalls that are issued
215
- from the current process. With KCOV_REMOTE_ENABLE it's possible to collect
216
- coverage for arbitrary parts of the kernel code, provided that those parts
217
- are annotated with kcov_remote_start()/kcov_remote_stop().
218
-
219
- This allows to collect coverage from two types of kernel background
220
- threads: the global ones, that are spawned during kernel boot in a limited
221
- number of instances (e.g. one USB hub_event() worker thread is spawned per
222
- USB HCD); and the local ones, that are spawned when a user interacts with
223
- some kernel interface (e.g. vhost workers); as well as from soft
224
- interrupts.
225
-
226
- To enable collecting coverage from a global background thread or from a
227
- softirq, a unique global handle must be assigned and passed to the
228
- corresponding kcov_remote_start() call. Then a userspace process can pass
229
- a list of such handles to the KCOV_REMOTE_ENABLE ioctl in the handles
230
- array field of the kcov_remote_arg struct. This will attach the used kcov
231
- device to the code sections, that are referenced by those handles.
232
-
233
- Since there might be many local background threads spawned from different
234
- userspace processes, we can't use a single global handle per annotation.
235
- Instead, the userspace process passes a non-zero handle through the
236
- common_handle field of the kcov_remote_arg struct. This common handle gets
237
- saved to the kcov_handle field in the current task_struct and needs to be
238
- passed to the newly spawned threads via custom annotations. Those threads
239
- should in turn be annotated with kcov_remote_start()/kcov_remote_stop().
240
-
241
- Internally kcov stores handles as u64 integers. The top byte of a handle
242
- is used to denote the id of a subsystem that this handle belongs to, and
243
- the lower 4 bytes are used to denote the id of a thread instance within
244
- that subsystem. A reserved value 0 is used as a subsystem id for common
245
- handles as they don't belong to a particular subsystem. The bytes 4-7 are
246
- currently reserved and must be zero. In the future the number of bytes
247
- used for the subsystem or handle ids might be increased.
248
-
249
- When a particular userspace process collects coverage via a common
250
- handle, kcov will collect coverage for each code section that is annotated
251
- to use the common handle obtained as kcov_handle from the current
252
- task_struct. However non common handles allow to collect coverage
253
- selectively from different subsystems.
223
+ Besides collecting coverage data from handlers of syscalls issued from a
224
+ userspace process, KCOV can also collect coverage for parts of the kernel
225
+ executing in other contexts - so-called "remote" coverage.
226
+
227
+ Using KCOV to collect remote coverage requires:
228
+
229
+ 1. Modifying kernel code to annotate the code section from where coverage
230
+ should be collected with ``kcov_remote_start `` and ``kcov_remote_stop ``.
231
+
232
+ 2. Using ``KCOV_REMOTE_ENABLE `` instead of ``KCOV_ENABLE `` in the userspace
233
+ process that collects coverage.
234
+
235
+ Both ``kcov_remote_start `` and ``kcov_remote_stop `` annotations and the
236
+ ``KCOV_REMOTE_ENABLE `` ioctl accept handles that identify particular coverage
237
+ collection sections. The way a handle is used depends on the context where the
238
+ matching code section executes.
239
+
240
+ KCOV supports collecting remote coverage from the following contexts:
241
+
242
+ 1. Global kernel background tasks. These are the tasks that are spawned during
243
+ kernel boot in a limited number of instances (e.g. one USB ``hub_event ``
244
+ worker is spawned per one USB HCD).
245
+
246
+ 2. Local kernel background tasks. These are spawned when a userspace process
247
+ interacts with some kernel interface and are usually killed when the process
248
+ exits (e.g. vhost workers).
249
+
250
+ 3. Soft interrupts.
251
+
252
+ For #1 and #3, a unique global handle must be chosen and passed to the
253
+ corresponding ``kcov_remote_start `` call. Then a userspace process must pass
254
+ this handle to ``KCOV_REMOTE_ENABLE `` in the ``handles `` array field of the
255
+ ``kcov_remote_arg `` struct. This will attach the used KCOV device to the code
256
+ section referenced by this handle. Multiple global handles identifying
257
+ different code sections can be passed at once.
258
+
259
+ For #2, the userspace process instead must pass a non-zero handle through the
260
+ ``common_handle `` field of the ``kcov_remote_arg `` struct. This common handle
261
+ gets saved to the ``kcov_handle `` field in the current ``task_struct `` and
262
+ needs to be passed to the newly spawned local tasks via custom kernel code
263
+ modifications. Those tasks should in turn use the passed handle in their
264
+ ``kcov_remote_start `` and ``kcov_remote_stop `` annotations.
265
+
266
+ KCOV follows a predefined format for both global and common handles. Each
267
+ handle is a ``u64 `` integer. Currently, only the one top and the lower 4 bytes
268
+ are used. Bytes 4-7 are reserved and must be zero.
269
+
270
+ For global handles, the top byte of the handle denotes the id of a subsystem
271
+ this handle belongs to. For example, KCOV uses ``1 `` as the USB subsystem id.
272
+ The lower 4 bytes of a global handle denote the id of a task instance within
273
+ that subsystem. For example, each ``hub_event `` worker uses the USB bus number
274
+ as the task instance id.
275
+
276
+ For common handles, a reserved value ``0 `` is used as a subsystem id, as such
277
+ handles don't belong to a particular subsystem. The lower 4 bytes of a common
278
+ handle identify a collective instance of all local tasks spawned by the
279
+ userspace process that passed a common handle to ``KCOV_REMOTE_ENABLE ``.
280
+
281
+ In practice, any value can be used for common handle instance id if coverage
282
+ is only collected from a single userspace process on the system. However, if
283
+ common handles are used by multiple processes, unique instance ids must be
284
+ used for each process. One option is to use the process id as the common
285
+ handle instance id.
286
+
287
+ The following program demonstrates using KCOV to collect coverage from both
288
+ local tasks spawned by the process and the global task that handles USB bus #1:
254
289
255
290
.. code-block :: c
256
291
0 commit comments