Back to home page

OSCL-LXR

 
 

    


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     }