0001 kcov: code coverage for fuzzing
0002 ===============================
0003
0004 kcov exposes kernel code coverage information in a form suitable for coverage-
0005 guided fuzzing (randomized testing). Coverage data of a running kernel is
0006 exported via the "kcov" debugfs file. Coverage collection is enabled on a task
0007 basis, and thus it can capture precise coverage of a single system call.
0008
0009 Note that kcov does not aim to collect as much coverage as possible. It aims
0010 to collect more or less stable coverage that is function of syscall inputs.
0011 To achieve this goal it does not collect coverage in soft/hard interrupts
0012 and instrumentation of some inherently non-deterministic parts of kernel is
0013 disabled (e.g. scheduler, locking).
0014
0015 kcov is also able to collect comparison operands from the instrumented code
0016 (this feature currently requires that the kernel is compiled with clang).
0017
0018 Prerequisites
0019 -------------
0020
0021 Configure the kernel with::
0022
0023 CONFIG_KCOV=y
0024
0025 CONFIG_KCOV requires gcc 6.1.0 or later.
0026
0027 If the comparison operands need to be collected, set::
0028
0029 CONFIG_KCOV_ENABLE_COMPARISONS=y
0030
0031 Profiling data will only become accessible once debugfs has been mounted::
0032
0033 mount -t debugfs none /sys/kernel/debug
0034
0035 Coverage collection
0036 -------------------
0037
0038 The following program demonstrates coverage collection from within a test
0039 program using kcov:
0040
0041 .. code-block:: c
0042
0043 #include <stdio.h>
0044 #include <stddef.h>
0045 #include <stdint.h>
0046 #include <stdlib.h>
0047 #include <sys/types.h>
0048 #include <sys/stat.h>
0049 #include <sys/ioctl.h>
0050 #include <sys/mman.h>
0051 #include <unistd.h>
0052 #include <fcntl.h>
0053 #include <linux/types.h>
0054
0055 #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
0056 #define KCOV_ENABLE _IO('c', 100)
0057 #define KCOV_DISABLE _IO('c', 101)
0058 #define COVER_SIZE (64<<10)
0059
0060 #define KCOV_TRACE_PC 0
0061 #define KCOV_TRACE_CMP 1
0062
0063 int main(int argc, char **argv)
0064 {
0065 int fd;
0066 unsigned long *cover, n, i;
0067
0068 /* A single fd descriptor allows coverage collection on a single
0069 * thread.
0070 */
0071 fd = open("/sys/kernel/debug/kcov", O_RDWR);
0072 if (fd == -1)
0073 perror("open"), exit(1);
0074 /* Setup trace mode and trace size. */
0075 if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
0076 perror("ioctl"), exit(1);
0077 /* Mmap buffer shared between kernel- and user-space. */
0078 cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
0079 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
0080 if ((void*)cover == MAP_FAILED)
0081 perror("mmap"), exit(1);
0082 /* Enable coverage collection on the current thread. */
0083 if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC))
0084 perror("ioctl"), exit(1);
0085 /* Reset coverage from the tail of the ioctl() call. */
0086 __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
0087 /* That's the target syscal call. */
0088 read(-1, NULL, 0);
0089 /* Read number of PCs collected. */
0090 n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
0091 for (i = 0; i < n; i++)
0092 printf("0x%lx\n", cover[i + 1]);
0093 /* Disable coverage collection for the current thread. After this call
0094 * coverage can be enabled for a different thread.
0095 */
0096 if (ioctl(fd, KCOV_DISABLE, 0))
0097 perror("ioctl"), exit(1);
0098 /* Free resources. */
0099 if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
0100 perror("munmap"), exit(1);
0101 if (close(fd))
0102 perror("close"), exit(1);
0103 return 0;
0104 }
0105
0106 After piping through addr2line output of the program looks as follows::
0107
0108 SyS_read
0109 fs/read_write.c:562
0110 __fdget_pos
0111 fs/file.c:774
0112 __fget_light
0113 fs/file.c:746
0114 __fget_light
0115 fs/file.c:750
0116 __fget_light
0117 fs/file.c:760
0118 __fdget_pos
0119 fs/file.c:784
0120 SyS_read
0121 fs/read_write.c:562
0122
0123 If a program needs to collect coverage from several threads (independently),
0124 it needs to open /sys/kernel/debug/kcov in each thread separately.
0125
0126 The interface is fine-grained to allow efficient forking of test processes.
0127 That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode,
0128 mmaps coverage buffer and then forks child processes in a loop. Child processes
0129 only need to enable coverage (disable happens automatically on thread end).
0130
0131 Comparison operands collection
0132 ------------------------------
0133
0134 Comparison operands collection is similar to coverage collection:
0135
0136 .. code-block:: c
0137
0138 /* Same includes and defines as above. */
0139
0140 /* Number of 64-bit words per record. */
0141 #define KCOV_WORDS_PER_CMP 4
0142
0143 /*
0144 * The format for the types of collected comparisons.
0145 *
0146 * Bit 0 shows whether one of the arguments is a compile-time constant.
0147 * Bits 1 & 2 contain log2 of the argument size, up to 8 bytes.
0148 */
0149
0150 #define KCOV_CMP_CONST (1 << 0)
0151 #define KCOV_CMP_SIZE(n) ((n) << 1)
0152 #define KCOV_CMP_MASK KCOV_CMP_SIZE(3)
0153
0154 int main(int argc, char **argv)
0155 {
0156 int fd;
0157 uint64_t *cover, type, arg1, arg2, is_const, size;
0158 unsigned long n, i;
0159
0160 fd = open("/sys/kernel/debug/kcov", O_RDWR);
0161 if (fd == -1)
0162 perror("open"), exit(1);
0163 if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
0164 perror("ioctl"), exit(1);
0165 /*
0166 * Note that the buffer pointer is of type uint64_t*, because all
0167 * the comparison operands are promoted to uint64_t.
0168 */
0169 cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
0170 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
0171 if ((void*)cover == MAP_FAILED)
0172 perror("mmap"), exit(1);
0173 /* Note KCOV_TRACE_CMP instead of KCOV_TRACE_PC. */
0174 if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP))
0175 perror("ioctl"), exit(1);
0176 __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
0177 read(-1, NULL, 0);
0178 /* Read number of comparisons collected. */
0179 n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
0180 for (i = 0; i < n; i++) {
0181 uint64_t ip;
0182
0183 type = cover[i * KCOV_WORDS_PER_CMP + 1];
0184 /* arg1 and arg2 - operands of the comparison. */
0185 arg1 = cover[i * KCOV_WORDS_PER_CMP + 2];
0186 arg2 = cover[i * KCOV_WORDS_PER_CMP + 3];
0187 /* ip - caller address. */
0188 ip = cover[i * KCOV_WORDS_PER_CMP + 4];
0189 /* size of the operands. */
0190 size = 1 << ((type & KCOV_CMP_MASK) >> 1);
0191 /* is_const - true if either operand is a compile-time constant.*/
0192 is_const = type & KCOV_CMP_CONST;
0193 printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, "
0194 "size: %lu, %s\n",
0195 ip, type, arg1, arg2, size,
0196 is_const ? "const" : "non-const");
0197 }
0198 if (ioctl(fd, KCOV_DISABLE, 0))
0199 perror("ioctl"), exit(1);
0200 /* Free resources. */
0201 if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
0202 perror("munmap"), exit(1);
0203 if (close(fd))
0204 perror("close"), exit(1);
0205 return 0;
0206 }
0207
0208 Note that the kcov modes (coverage collection or comparison operands) are
0209 mutually exclusive.
0210
0211 Remote coverage collection
0212 --------------------------
0213
0214 With KCOV_ENABLE coverage is collected only for syscalls that are issued
0215 from the current process. With KCOV_REMOTE_ENABLE it's possible to collect
0216 coverage for arbitrary parts of the kernel code, provided that those parts
0217 are annotated with kcov_remote_start()/kcov_remote_stop().
0218
0219 This allows to collect coverage from two types of kernel background
0220 threads: the global ones, that are spawned during kernel boot in a limited
0221 number of instances (e.g. one USB hub_event() worker thread is spawned per
0222 USB HCD); and the local ones, that are spawned when a user interacts with
0223 some kernel interface (e.g. vhost workers); as well as from soft
0224 interrupts.
0225
0226 To enable collecting coverage from a global background thread or from a
0227 softirq, a unique global handle must be assigned and passed to the
0228 corresponding kcov_remote_start() call. Then a userspace process can pass
0229 a list of such handles to the KCOV_REMOTE_ENABLE ioctl in the handles
0230 array field of the kcov_remote_arg struct. This will attach the used kcov
0231 device to the code sections, that are referenced by those handles.
0232
0233 Since there might be many local background threads spawned from different
0234 userspace processes, we can't use a single global handle per annotation.
0235 Instead, the userspace process passes a non-zero handle through the
0236 common_handle field of the kcov_remote_arg struct. This common handle gets
0237 saved to the kcov_handle field in the current task_struct and needs to be
0238 passed to the newly spawned threads via custom annotations. Those threads
0239 should in turn be annotated with kcov_remote_start()/kcov_remote_stop().
0240
0241 Internally kcov stores handles as u64 integers. The top byte of a handle
0242 is used to denote the id of a subsystem that this handle belongs to, and
0243 the lower 4 bytes are used to denote the id of a thread instance within
0244 that subsystem. A reserved value 0 is used as a subsystem id for common
0245 handles as they don't belong to a particular subsystem. The bytes 4-7 are
0246 currently reserved and must be zero. In the future the number of bytes
0247 used for the subsystem or handle ids might be increased.
0248
0249 When a particular userspace process collects coverage via a common
0250 handle, kcov will collect coverage for each code section that is annotated
0251 to use the common handle obtained as kcov_handle from the current
0252 task_struct. However non common handles allow to collect coverage
0253 selectively from different subsystems.
0254
0255 .. code-block:: c
0256
0257 /* Same includes and defines as above. */
0258
0259 struct kcov_remote_arg {
0260 __u32 trace_mode;
0261 __u32 area_size;
0262 __u32 num_handles;
0263 __aligned_u64 common_handle;
0264 __aligned_u64 handles[0];
0265 };
0266
0267 #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
0268 #define KCOV_DISABLE _IO('c', 101)
0269 #define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg)
0270
0271 #define COVER_SIZE (64 << 10)
0272
0273 #define KCOV_TRACE_PC 0
0274
0275 #define KCOV_SUBSYSTEM_COMMON (0x00ull << 56)
0276 #define KCOV_SUBSYSTEM_USB (0x01ull << 56)
0277
0278 #define KCOV_SUBSYSTEM_MASK (0xffull << 56)
0279 #define KCOV_INSTANCE_MASK (0xffffffffull)
0280
0281 static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst)
0282 {
0283 if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK)
0284 return 0;
0285 return subsys | inst;
0286 }
0287
0288 #define KCOV_COMMON_ID 0x42
0289 #define KCOV_USB_BUS_NUM 1
0290
0291 int main(int argc, char **argv)
0292 {
0293 int fd;
0294 unsigned long *cover, n, i;
0295 struct kcov_remote_arg *arg;
0296
0297 fd = open("/sys/kernel/debug/kcov", O_RDWR);
0298 if (fd == -1)
0299 perror("open"), exit(1);
0300 if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
0301 perror("ioctl"), exit(1);
0302 cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
0303 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
0304 if ((void*)cover == MAP_FAILED)
0305 perror("mmap"), exit(1);
0306
0307 /* Enable coverage collection via common handle and from USB bus #1. */
0308 arg = calloc(1, sizeof(*arg) + sizeof(uint64_t));
0309 if (!arg)
0310 perror("calloc"), exit(1);
0311 arg->trace_mode = KCOV_TRACE_PC;
0312 arg->area_size = COVER_SIZE;
0313 arg->num_handles = 1;
0314 arg->common_handle = kcov_remote_handle(KCOV_SUBSYSTEM_COMMON,
0315 KCOV_COMMON_ID);
0316 arg->handles[0] = kcov_remote_handle(KCOV_SUBSYSTEM_USB,
0317 KCOV_USB_BUS_NUM);
0318 if (ioctl(fd, KCOV_REMOTE_ENABLE, arg))
0319 perror("ioctl"), free(arg), exit(1);
0320 free(arg);
0321
0322 /*
0323 * Here the user needs to trigger execution of a kernel code section
0324 * that is either annotated with the common handle, or to trigger some
0325 * activity on USB bus #1.
0326 */
0327 sleep(2);
0328
0329 n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
0330 for (i = 0; i < n; i++)
0331 printf("0x%lx\n", cover[i + 1]);
0332 if (ioctl(fd, KCOV_DISABLE, 0))
0333 perror("ioctl"), exit(1);
0334 if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
0335 perror("munmap"), exit(1);
0336 if (close(fd))
0337 perror("close"), exit(1);
0338 return 0;
0339 }