kcov: support comparison operands collection
Subsystems (6)
Alexander Potapenko
reviewerPATCH v4 1/3 kcov: support comparison operands collection
From: Victor Chibotaru <[email protected]>
Enables kcov to collect comparison operands from instrumented code. This is done by using Clang's -fsanitize=trace-cmp instrumentation (currently not available for GCC).
The comparison operands help a lot in fuzz testing. E.g. they are used in Syzkaller to cover the interiors of conditional statements with way less attempts and thus make previously unreachable code reachable.
To allow separate collection of coverage and comparison operands two different work modes are implemented. Mode selection is now done via a KCOV_ENABLE ioctl call with corresponding argument value.
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
Syzkaller:
https://github.com/google/syzkaller
v4: - avoid merge conflict with https://patchwork.kernel.org/patch/9978383/
v3: - fix Mark Rutland's comments: drop KCOV_CMP_SIZEx,
added canonicalize_ip(), other minor changes
v2: - fix wording (as suggested by Mark Rutland)
- store caller PCs (as suggested by Andrey Konovalov)
--- include/linux/kcov.h | 12 ++- include/uapi/linux/kcov.h | 24 ++++++ kernel/kcov.c | 214 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 211 insertions(+), 39 deletions(-)
include/linux/kcov.h +8 -4
@@ -7,19 +7,23 @@ struct task_struct;#ifdef CONFIG_KCOV -void kcov_task_init (struct task_struct *t); -void kcov_task_exit (struct task_struct *t); -enum kcov_mode {/* Coverage collection is not enabled yet. */ KCOV_MODE_DISABLED =0 , +/* KCOV was initialized, but tracing mode hasn't been chosen yet. */ +KCOV_MODE_INIT =1 ,/* * Tracing coverage collection mode. * Covered PCs are collected in a per-task buffer. */ -KCOV_MODE_TRACE =1 , +KCOV_MODE_TRACE_PC =2 , +/* Collecting comparison operands mode. */ +KCOV_MODE_TRACE_CMP =3 , }; +void kcov_task_init (struct task_struct *t); +void kcov_task_exit (struct task_struct *t); +#else static inline void kcov_task_init (struct task_struct *t) {}
include/uapi/linux/kcov.h +24 -0
@@ -7,4 +7,28 @@#define KCOV_ENABLE _IO('c', 100)#define KCOV_DISABLE _IO('c', 101) +enum { +/* +* Tracing coverage collection mode. +* Covered PCs are collected in a per-task buffer. +* In new KCOV version the mode is chosen by calling +* ioctl(fd, KCOV_ENABLE, mode). In older versions the mode argument +* was supposed to be 0 in such a call. So, for reasons of backward +* compatibility, we have chosen the value KCOV_TRACE_PC to be 0. +*/ +KCOV_TRACE_PC =0 , +/* Collecting comparison operands mode. */ +KCOV_TRACE_CMP =1 , +}; + +/* +* The format for the types of collected comparisons. +* +* Bit 0 shows whether one of the arguments is a compile-time constant. +* Bits 1 & 2 contain log2 of the argument size, up to 8 bytes. +*/ +#define KCOV_CMP_CONST (1 << 0) +#define KCOV_CMP_SIZE (n) ((n) << 1) +#define KCOV_CMP_MASK KCOV_CMP_SIZE(3) +#endif /* _LINUX_KCOV_IOCTLS_H */
kernel/kcov.c +179 -35
292 lines not on this page · raw message
Signature · 7 lines
2.15.0.rc0.271.g36b669edcc-goog -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Alexander Potapenko
reviewerPATCH v4 2/3 Makefile: support flag -fsanitizer-coverage=trace-cmp
From: Victor Chibotaru <[email protected]>
The flag enables Clang instrumentation of comparison operations (currently not supported by GCC). This instrumentation is needed by the new KCOV device to collect comparison operands.
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
v4: - drop CFLAGS_KCOV_COMPS for real (revert scripts/Makefile.lib)
v3: - Andrey Ryabinin's comments: reinstated scripts/Makefile.kcov
and moved CFLAGS_KCOV there, dropped CFLAGS_KCOV_COMPS
- allow building with GCC
v2: - updated KCOV_ENABLE_COMPARISONS description
--- Makefile | 3 +-- lib/Kconfig.debug | 10 ++++++++++ scripts/Makefile.kcov | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 scripts/Makefile.kcov
Makefile +1 -2
@@ -373,8 +373,6 @@ CFLAGS_KERNEL = AFLAGS_KERNEL = LDFLAGS_vmlinux = CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,) -CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) - # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE := \
@@ -657,6 +655,7 @@ ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC) $(KBUILD_CFLA KBUILD_AFLAGS += -DCC_HAVE_ASM_GOTO endif +include scripts/Makefile.kcov include scripts/Makefile.gcc-plugins ifdef CONFIG_READABLE_ASM
lib/Kconfig.debug +10 -0
@@ -759,6 +759,16 @@ config KCOV For more details, see Documentation/dev-tools/kcov.rst. +config KCOV_ENABLE_COMPARISONS + bool "Enable comparison operands collection by KCOV" + depends on KCOV + default n + help + KCOV also exposes operands of every comparison in the instrumented + code along with operand sizes and PCs of the comparison instructions. + These operands can be used by fuzzing engines to improve the quality + of fuzzing coverage. + config KCOV_INSTRUMENT_ALL bool "Instrument all code by default" depends on KCOV
scripts/Makefile.kcov new file +7 -0
@@ -0,0 +1,7 @@ +ifdef CONFIG_KCOV +CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) +ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) +CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=trace-cmp,) +endif + +endif
Signature · 7 lines
2.15.0.rc0.271.g36b669edcc-goog -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Alexander Potapenko
reviewerPATCH v4 3/3 kcov: update documentation
In reply to Alexander PotapenkoFrom: Victor Chibotaru <[email protected]>
The updated documentation describes new KCOV mode for collecting comparison operands.
---
v3: - update the test program to match new uapi
v2: - reflect the changes to kcov.c in the test program.
--- Documentation/dev-tools/kcov.rst | 99 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)
Documentation/dev-tools/kcov.rst +95 -4
@@ -12,19 +12,30 @@ To achieve this goal it does not collect coverage in soft/hard interrupts and instrumentation of some inherently non-deterministic parts of kernel is disabled (e.g. scheduler, locking). -Usage ------ +kcov is also able to collect comparison operands from the instrumented code +(this feature currently requires that the kernel is compiled with clang). + +Prerequisites +------------- Configure the kernel with:: CONFIG_KCOV=y CONFIG_KCOV requires gcc built on revision 231296 or later. + +If the comparison operands need to be collected, set:: + + CONFIG_KCOV_ENABLE_COMPARISONS=y + Profiling data will only become accessible once debugfs has been mounted:: mount -t debugfs none /sys/kernel/debug -The following program demonstrates kcov usage from within a test program: +Coverage collection +------------------- +The following program demonstrates coverage collection from within a test +program using kcov: .. code-block:: c
@@ -44,6 +55,9 @@ The following program demonstrates kcov usage from within a test program: #define KCOV_DISABLE _IO('c', 101) #define COVER_SIZE (64<<10) + #define KCOV_TRACE_PC 0 + #define KCOV_TRACE_CMP 1 + int main(int argc, char **argv) { int fd;
@@ -64,7 +78,7 @@ The following program demonstrates kcov usage from within a test program: if ((void*)cover == MAP_FAILED) perror("mmap"), exit(1); /* Enable coverage collection on the current thread. */ - if (ioctl(fd, KCOV_ENABLE, 0)) + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) perror("ioctl"), exit(1); /* Reset coverage from the tail of the ioctl() call. */ __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
@@ -111,3 +125,80 @@ The interface is fine-grained to allow efficient forking of test processes. That is, a parent process opens /sys/kernel/debug/kcov, enables trace mode, mmaps coverage buffer and then forks child processes in a loop. Child processes only need to enable coverage (disable happens automatically on thread end). + +Comparison operands collection +------------------------------ +Comparison operands collection is similar to coverage collection: + +.. code-block:: c + + /* Same includes and defines as above. */ + + /* Number of 64-bit words per record. */ + #define KCOV_WORDS_PER_CMP 4 + + /* + * The format for the types of collected comparisons. + * + * Bit 0 shows whether one of the arguments is a compile-time constant. + * Bits 1 & 2 contain log2 of the argument size, up to 8 bytes. + */ + + #define KCOV_CMP_CONST (1 << 0) + #define KCOV_CMP_SIZE(n) ((n) << 1) + #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) + + int main(int argc, char **argv) + { + int fd; + uint64_t *cover, type, arg1, arg2, is_const, size; + unsigned long n, i; + + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + /* + * Note that the buffer pointer is of type uint64_t*, because all + * the comparison operands are promoted to uint64_t. + */ + cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + /* Note KCOV_TRACE_CMP instead of KCOV_TRACE_PC. */ + if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP)) + perror("ioctl"), exit(1); + __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); + read(-1, NULL, 0); + /* Read number of comparisons collected. */ + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) { + type = cover[i * KCOV_WORDS_PER_CMP + 1]; + /* arg1 and arg2 - operands of the comparison. */ + arg1 = cover[i * KCOV_WORDS_PER_CMP + 2]; + arg2 = cover[i * KCOV_WORDS_PER_CMP + 3]; + /* ip - caller address. */ + ip = cover[i * KCOV_WORDS_PER_CMP + 4]; + /* size of the operands. */ + size = 1 << ((type & KCOV_CMP_MASK) >> 1); + /* is_const - true if either operand is a compile-time constant.*/ + is_const = type & KCOV_CMP_CONST; + printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, " + "size: %lu, %s\n", + ip, type, arg1, arg2, size, + is_const ? "const" : "non-const"); + } + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + /* Free resources. */ + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } + +Note that the kcov modes (coverage collection or comparison operands) are +mutually exclusive.
Signature · 7 lines
2.15.0.rc0.271.g36b669edcc-goog -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>