kcov: support comparison operands collection
Subsystems (6)
Dmitry Vyukov
reviewercover kcov: support comparison operands collection
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
Victor Chibotaru (3):
kcov: support comparison operands collection
Makefile: support flag -fsanitizer-coverage=trace-cmp
kcov: update documentation
Documentation/dev-tools/kcov.rst | 94 +++++++++++++++++- Makefile | 5 +- include/linux/kcov.h | 12 ++- include/uapi/linux/kcov.h | 32 ++++++ kernel/kcov.c | 203 ++++++++++++++++++++++++++++++++------- lib/Kconfig.debug | 8 ++ scripts/Makefile.kcov | 6 ++ scripts/Makefile.lib | 6 ++ 8 files changed, 322 insertions(+), 44 deletions(-) create mode 100644 scripts/Makefile.kcov
Signature · 7 lines
2.14.1.581.gf28d330327-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>
Dmitry Vyukov
reviewerPATCH 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
--- include/linux/kcov.h | 12 ++- include/uapi/linux/kcov.h | 32 ++++++++ kernel/kcov.c | 203 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 209 insertions(+), 38 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 +32 -0
@@ -7,4 +7,36 @@#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 , +}; + +/* +* Defines the format for the types of collected comparisons. +*/ +enum kcov_cmp_type { +/* +* LSB shows whether one of the arguments is a compile-time constant. +*/ +KCOV_CMP_CONST =1 , +/* +* Second and third LSBs contain the size of arguments (1/2/4/8 bytes). +*/ +KCOV_CMP_SIZE1 =0 , +KCOV_CMP_SIZE2 =2 , +KCOV_CMP_SIZE4 =4 , +KCOV_CMP_SIZE8 =6 , +KCOV_CMP_SIZE_MASK =6 , +}; +#endif /* _LINUX_KCOV_IOCTLS_H */
kernel/kcov.c +169 -34
281 lines not on this page · raw message
Signature · 7 lines
2.14.1.581.gf28d330327-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>
Hi,
Quoted 5 lines · Dmitry Vyukov
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).
What's needed to build the kernel with Clang these days?
I was under the impression that it still wasn't possible to build arm64 with clang due to a number of missing features (e.g. the %a assembler output template).
Quoted 20 lines
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>
Quoted 3 lines
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
How stable is this?
The comment at the end says "This interface is a subject to change."
[...]
kernel/kcov.c line 25
Quoted 10 lines
kernel/kcov.c +2
-0@@ -21,13 +21,21 @@#include <linux/kcov.h> #include <asm/setup.h> +/* Number of words written per one comparison. */ +#define KCOV_WORDS_PER_CMP 3
Could you please expand the comment to cover what a "word" is?
Generally, "word" is an ambiguous term, and it's used inconsitently in this file as of this patch. For comparison coverage, a "word" is assumed to always be 64-bit, (which makes sxense given 64-bit comparisons), but for branch coverage a "word" refers to an unsigned long, which would be 32-bit on a 32-bit platform.
[...]
kernel/kcov.c struct kcov { · line 58
Quoted 1 line
kernel/kcov.c +1
-0+static bool check_kcov_mode (enum kcov_mode needed_mode,struct task_struct *t)
Perhaps kcov_mode_is_active()?
That would better describe what is being checked.
Quoted 11 lines
kernel/kcov.c +11
-0+{ +enum kcov_mode mode; + +/* +* We are interested in code coverage as a function of a syscall inputs, +* so we ignore code executed in interrupts. +*/ +if (!t || !in_task ()) +return false; + mode =READ_ONCE (t->kcov_mode); +/*
kernel/kcov.c struct kcov { · line 79
Quoted 10 lines
kernel/kcov.c +10
-0+* There is some code that runs in interrupts but for which +* in_interrupt() returns false (e.g. preempt_schedule_irq()). +* READ_ONCE()/barrier() effectively provides load-acquire wrt +* interrupts, there are paired barrier()/WRITE_ONCE() in +* kcov_ioctl_locked(). +*/ +barrier (); +if (mode != needed_mode) +return false; +return true;
This would be simlper as:
barrier(); return mode == needed_mode;
[...]
Quoted 7 lines
kernel/kcov.c +7
-0+#ifdef CONFIG_KCOV_ENABLE_COMPARISONS +static void write_comp_data (u64 type,u64 arg1,u64 arg2) +{ +struct task_struct *t; +u64 *area; +u64 count, start_index, end_pos, max_pos; +
kernel/kcov.c struct kcov { · line 127
Quoted 10 lines
kernel/kcov.c +10
-0+ t = current; +if (!check_kcov_mode (KCOV_MODE_TRACE_CMP , t)) +return ; + +/* +* We write all comparison arguments and types as u64. +* The buffer was allocated for t->kcov_size unsigned longs. +*/ + area = (u64 *)t->kcov_area; + max_pos = t->kcov_size *sizeof (unsigned long );
Perhaps it would make more sense for k->kcov_size to be in bytes, if different options will have differing record sizes?
kernel/kcov.c struct kcov { · line 131
Quoted 4 lines
kernel/kcov.c +4
-0+ + count =READ_ONCE (area[0 ]); + +/* Every record is KCOV_WORDS_PER_CMP words. */
As above, please be explicit about what a "word" is, or avoid using "word" terminology.
Thanks,
Mark.
Alexander Potapenko
reviewerQuoted 8 lines · Mark Rutland
Hi,
Quoted 5 lines · Dmitry Vyukov
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).
Hi Mark,
Quoted 1 line
What's needed to build the kernel with Clang these days?
Shameless plug: https://github.com/ramosian-glider/clang-kernel-build
We are currently one patch away from booting the Clang-built kernel on x86. The patch in my repo is far from perfect, Josh Poimboeuf has a better
one at https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git/log/?h=ASM_CALL
Quoted 3 lines
I was under the impression that it still wasn't possible to build arm64 with clang due to a number of missing features (e.g. the %a assembler output template).
Sorry, I haven't experimented with ARM much. Nick Desaulniers or Greg Hackmann should know the details, I'll ask them.
I think the current status is "almost there".
Quoted 122 lines
Quoted 20 lines
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>Quoted 3 lines
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flowHow stable is this?
The comment at the end says "This interface is a subject to change."
[...]
kernel/kcov.c line 25
Quoted 10 lines
kernel/kcov.c +2
-0@@ -21,13 +21,21 @@#include <linux/kcov.h> #include <asm/setup.h> +/* Number of words written per one comparison. */ +#define KCOV_WORDS_PER_CMP 3Could you please expand the comment to cover what a "word" is?
Generally, "word" is an ambiguous term, and it's used inconsitently in this file as of this patch. For comparison coverage, a "word" is assumed to always be 64-bit, (which makes sxense given 64-bit comparisons), but for branch coverage a "word" refers to an unsigned long, which would be 32-bit on a 32-bit platform.
[...]
kernel/kcov.c struct kcov { · line 58
Quoted 1 line
kernel/kcov.c +1
-0+static bool check_kcov_mode (enum kcov_mode needed_mode,struct task_struct *t)Perhaps
kcov_mode_is_active()?That would better describe what is being checked.
Quoted 11 lines
kernel/kcov.c +11
-0+{ +enum kcov_mode mode; + +/* +* We are interested in code coverage as a function of a syscall inputs, +* so we ignore code executed in interrupts. +*/ +if (!t || !in_task ()) +return false; + mode =READ_ONCE (t->kcov_mode); +/* kernel/kcov.c struct kcov { · line 79
Quoted 10 lines
kernel/kcov.c +10
-0+* There is some code that runs in interrupts but for which +* in_interrupt() returns false (e.g. preempt_schedule_irq()). +* READ_ONCE()/barrier() effectively provides load-acquire wrt +* interrupts, there are paired barrier()/WRITE_ONCE() in +* kcov_ioctl_locked(). +*/ +barrier (); +if (mode != needed_mode) +return false; +return true;This would be simlper as:
barrier(); return mode == needed_mode;[...]
Quoted 7 lines
kernel/kcov.c +7
-0+#ifdef CONFIG_KCOV_ENABLE_COMPARISONS +static void write_comp_data (u64 type,u64 arg1,u64 arg2) +{ +struct task_struct *t; +u64 *area; +u64 count, start_index, end_pos, max_pos; +kernel/kcov.c struct kcov { · line 127
Quoted 10 lines
kernel/kcov.c +10
-0+ t = current; +if (!check_kcov_mode (KCOV_MODE_TRACE_CMP , t)) +return ; + +/* +* We write all comparison arguments and types as u64. +* The buffer was allocated for t->kcov_size unsigned longs. +*/ + area = (u64 *)t->kcov_area; + max_pos = t->kcov_size *sizeof (unsigned long );Perhaps it would make more sense for
k->kcov_sizeto be in bytes, if different options will have differing record sizes?kernel/kcov.c struct kcov { · line 131
Quoted 4 lines
kernel/kcov.c +4
-0+ + count =READ_ONCE (area[0 ]); + +/* Every record is KCOV_WORDS_PER_CMP words. */ As above, please be explicit about what a "word" is, or avoid using "word" terminology.
Thanks,
Mark.--
You received this message because you are subscribed to the Google Groups "syzkaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
Signature · 16 lines
Alexander Potapenko Software Engineer Google Germany GmbH Erika-Mann-Straße, 33 80636 München Geschäftsführer: Paul Manicle, Halimah DeLaine Prado Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg -- 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>
Dmitry Vyukov
reviewerQuoted 39 lines · Mark Rutland
Hi,
Quoted 5 lines · Dmitry Vyukov
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).
What's needed to build the kernel with Clang these days?
I was under the impression that it still wasn't possible to build arm64 with clang due to a number of missing features (e.g. the %a assembler output template).
Quoted 20 lines
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>Quoted 3 lines
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
Quoted 3 lines
How stable is this?
The comment at the end says "This interface is a subject to change."
The intention is that this is not subject to change anymore (since we are using it in kernel).
I've mailed change to docs: https://reviews.llvm.org/D37303
FWIW, there is patch in flight that adds this instrumentation to gcc:
https://groups.google.com/forum/#!topic/syzkaller/CSLynn6nI-A
It seems to be stalled on review phase, though.
--
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>
Quoted 11 lines · Dmitry Vyukov
Quoted 10 lines · Mark Rutland
Quoted 8 lines · Dmitry Vyukov
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).
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
Quoted 7 lines
Quoted 3 lines
How stable is this?
The comment at the end says "This interface is a subject to change."
The intention is that this is not subject to change anymore (since we are using it in kernel).
I've mailed change to docs: https://reviews.llvm.org/D37303
Ok; thanks for confirming.
Quoted 3 lines
FWIW, there is patch in flight that adds this instrumentation to gcc:
https://groups.google.com/forum/#!topic/syzkaller/CSLynn6nI-A
It seems to be stalled on review phase, though.
Let's hope it gets unblocked soon. :)
Thanks,
Mark.
--
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>
Dmitry Vyukov
reviewerQuoted 45 lines · Dmitry Vyukov
Quoted 39 lines · Mark Rutland
Hi,
Quoted 5 lines · Dmitry Vyukov
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).
What's needed to build the kernel with Clang these days?
I was under the impression that it still wasn't possible to build arm64 with clang due to a number of missing features (e.g. the %a assembler output template).
Quoted 20 lines
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>Quoted 3 lines
---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flowQuoted 3 lines
How stable is this?
The comment at the end says "This interface is a subject to change."
Quoted 7 lines
The intention is that this is not subject to change anymore (since we are using it in kernel).
I've mailed change to docs: https://reviews.llvm.org/D37303FWIW, there is patch in flight that adds this instrumentation to gcc:
https://groups.google.com/forum/#!topic/syzkaller/CSLynn6nI-A
It seems to be stalled on review phase, though.
Good news is that this is submitted to gcc in 251801.
--
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>
Quoted 238 lines · Dmitry Vyukov' via syzkaller
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
Syzkaller:
https://github.com/google/syzkaller--- include/linux/kcov.h | 12 ++- include/uapi/linux/kcov.h | 32 ++++++++ kernel/kcov.c | 203 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 209 insertions(+), 38 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 +32
-0@@ -7,4 +7,36 @@#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 , +}; + +/* +* Defines the format for the types of collected comparisons. +*/ +enum kcov_cmp_type { +/* +* LSB shows whether one of the arguments is a compile-time constant. +*/ +KCOV_CMP_CONST =1 , +/* +* Second and third LSBs contain the size of arguments (1/2/4/8 bytes). +*/ +KCOV_CMP_SIZE1 =0 , +KCOV_CMP_SIZE2 =2 , +KCOV_CMP_SIZE4 =4 , +KCOV_CMP_SIZE8 =6 , +KCOV_CMP_SIZE_MASK =6 , +}; +#endif /* _LINUX_KCOV_IOCTLS_H */ kernel/kcov.c +61
-28@@ -21,13 +21,21 @@#include <linux/kcov.h> #include <asm/setup.h> +/* Number of words written per one comparison. */ +#define KCOV_WORDS_PER_CMP 3 +/* * kcov descriptor (one per opened debugfs file). * State transitions of the descriptor: * - initial state after open() * - then there must be a single ioctl(KCOV_INIT_TRACE) call * - then, mmap() call (several calls are allowed but not useful) -* - then, repeated enable/disable for a task (only one task a time allowed) +* - then, ioctl(KCOV_ENABLE, arg), where arg is +* KCOV_TRACE_PC - to trace only the PCs +* or +* KCOV_TRACE_CMP - to trace only the comparison operands +* - then, ioctl(KCOV_DISABLE) to disable the task. +* Enabling/disabling ioctls can be repeated (only one task a time allowed). */ struct kcov {/* @@ -47,6 +55,30 @@ struct kcov {struct task_struct *t; }; +static bool check_kcov_mode (enum kcov_mode needed_mode,struct task_struct *t) +{ +enum kcov_mode mode; + +/* +* We are interested in code coverage as a function of a syscall inputs, +* so we ignore code executed in interrupts. +*/ +if (!t || !in_task ()) +return false; + mode =READ_ONCE (t->kcov_mode); +/* +* There is some code that runs in interrupts but for which +* in_interrupt() returns false (e.g. preempt_schedule_irq()). +* READ_ONCE()/barrier() effectively provides load-acquire wrt +* interrupts, there are paired barrier()/WRITE_ONCE() in +* kcov_ioctl_locked(). +*/ +barrier (); +if (mode != needed_mode) +return false; +return true; +} +/* * Entry point from instrumented code. * This is called once per basic-block/edge. @@ -54,44 +86,136 @@ struct kcov { void notrace __sanitizer_cov_trace_pc(void) {struct task_struct *t; -enum kcov_mode mode; +unsigned long *area; +unsigned long ip = _RET_IP_; +unsigned long pos; t = current; -/* -* We are interested in code coverage as a function of a syscall inputs, -* so we ignore code executed in interrupts. -*/ -if (!t || !in_task ()) +if (!check_kcov_mode (KCOV_MODE_TRACE_PC , t))return ; - mode =READ_ONCE (t->kcov_mode); -if (mode ==KCOV_MODE_TRACE ) { -unsigned long *area; -unsigned long pos; -unsigned long ip = _RET_IP_;#ifdef CONFIG_RANDOMIZE_BASE - ip -=kaslr_offset (); + ip -=kaslr_offset ();#endif -/* -* There is some code that runs in interrupts but for which -* in_interrupt() returns false (e.g. preempt_schedule_irq()). -* READ_ONCE()/barrier() effectively provides load-acquire wrt -* interrupts, there are paired barrier()/WRITE_ONCE() in -* kcov_ioctl_locked(). -*/ -barrier (); - area = t->kcov_area; -/* The first word is number of subsequent PCs. */ - pos =READ_ONCE (area[0 ]) +1 ; -if (likely (pos < t->kcov_size)) { - area[pos] = ip; -WRITE_ONCE (area[0 ], pos); - } + area = t->kcov_area; +/* The first word is number of subsequent PCs. */ + pos =READ_ONCE (area[0 ]) +1 ; +if (likely (pos < t->kcov_size)) { + area[pos] = ip; +WRITE_ONCE (area[0 ], pos); } }EXPORT_SYMBOL (__sanitizer_cov_trace_pc); +#ifdef CONFIG_KCOV_ENABLE_COMPARISONS +static void write_comp_data (u64 type,u64 arg1,u64 arg2) +{ +struct task_struct *t; +u64 *area; +u64 count, start_index, end_pos, max_pos; + + t = current; +if (!check_kcov_mode (KCOV_MODE_TRACE_CMP , t)) +return ; + +/* +* We write all comparison arguments and types as u64. +* The buffer was allocated for t->kcov_size unsigned longs. +*/ + area = (u64 *)t->kcov_area; + max_pos = t->kcov_size *sizeof (unsigned long );
kernel/kcov.c struct kcov { · line 137
Quoted 10 lines
kernel/kcov.c +10
-0+ + count =READ_ONCE (area[0 ]); + +/* Every record is KCOV_WORDS_PER_CMP words. */ + start_index =1 + count *KCOV_WORDS_PER_CMP ; + end_pos = (start_index +KCOV_WORDS_PER_CMP ) *sizeof (u64); +if (likely (end_pos <= max_pos)) { + area[start_index] = type; + area[start_index +1 ] = arg1; + area[start_index +2 ] = arg2;
It might make sense to also store PC of the comparison here. This will allow to trace operands of some specific comparisons.
Quoted 159 lines
kernel/kcov.c +98
-7+ WRITE_ONCE(area[0], count + 1); + } +} + +void notrace __sanitizer_cov_trace_cmp1(u8 arg1, u8 arg2) +{ +write_comp_data (KCOV_CMP_SIZE1 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp1); + +void notrace __sanitizer_cov_trace_cmp2(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE2 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp2); + +void notrace __sanitizer_cov_trace_cmp4(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE4 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp4); + +void notrace __sanitizer_cov_trace_cmp8(u64 arg1, u64 arg2) +{ +write_comp_data (KCOV_CMP_SIZE8 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp8); + +void notrace __sanitizer_cov_trace_const_cmp1(u8 arg1, u8 arg2) +{ +write_comp_data (KCOV_CMP_SIZE1 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp1); + +void notrace __sanitizer_cov_trace_const_cmp2(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE2 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp2); + +void notrace __sanitizer_cov_trace_const_cmp4(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE4 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp4); + +void notrace __sanitizer_cov_trace_const_cmp8(u64 arg1, u64 arg2) +{ +write_comp_data (KCOV_CMP_SIZE8 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp8); + +void notrace __sanitizer_cov_trace_switch(u64 val, u64 *cases) +{ +u64 i; +u64 count = cases[0 ]; +u64 size = cases[1 ]; +u64 type =KCOV_CMP_CONST ; + +switch (size) { +case 8 : + type |=KCOV_CMP_SIZE1 ; +break ; +case 16 : + type |=KCOV_CMP_SIZE2 ; +break ; +case 32 : + type |=KCOV_CMP_SIZE4 ; +break ; +case 64 : + type |=KCOV_CMP_SIZE8 ; +break ; +default : +return ; + } +for (i =0 ; i < count; i++) +write_comp_data (type, cases[i +2 ], val); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_switch); +#endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */ +static void kcov_get (struct kcov *kcov) {atomic_inc (&kcov->refcount);@@ -128,6 +252,7 @@ void kcov_task_exit(struct task_struct *t)/* Just to not leave dangling references behind. */ kcov_task_init (t); kcov->t =NULL ; + kcov->mode =KCOV_MODE_INIT ;spin_unlock (&kcov->lock);kcov_put (kcov); }@@ -146,7 +271,7 @@ static int kcov_mmap(struct file *filep, struct vm_area_struct *vma)spin_lock (&kcov->lock); size = kcov->size *sizeof (unsigned long ); -if (kcov->mode ==KCOV_MODE_DISABLED || vma->vm_pgoff !=0 || +if (kcov->mode !=KCOV_MODE_INIT || vma->vm_pgoff !=0 || vma->vm_end - vma->vm_start != size) { res = -EINVAL ; goto exit;@@ -175,6 +300,7 @@ static int kcov_open(struct inode *inode, struct file *filep) kcov =kzalloc (sizeof (*kcov),GFP_KERNEL );if (!kcov)return -ENOMEM ; + kcov->mode =KCOV_MODE_DISABLED ;atomic_set (&kcov->refcount,1 );spin_lock_init (&kcov->lock); filep->private_data = kcov;@@ -210,7 +336,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,if (size <2 || size >INT_MAX /sizeof (unsigned long ))return -EINVAL ; kcov->size = size; - kcov->mode =KCOV_MODE_TRACE ; + kcov->mode =KCOV_MODE_INIT ;return 0 ;case KCOV_ENABLE :/* @@ -220,17 +346,25 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,* at task exit or voluntary by KCOV_DISABLE. After that it can * be enabled for another task. */ - unused = arg; -if (unused !=0 || kcov->mode ==KCOV_MODE_DISABLED || - kcov->area ==NULL ) +if (kcov->mode !=KCOV_MODE_INIT || !kcov->area)return -EINVAL ;if (kcov->t !=NULL )return -EBUSY ; +if (arg ==KCOV_TRACE_PC ) + kcov->mode =KCOV_MODE_TRACE_PC ; + else if (arg == KCOV_TRACE_CMP) +#ifdef CONFIG_KCOV_ENABLE_COMPARISONS + kcov->mode =KCOV_MODE_TRACE_CMP ; +#else +return -ENOTSUPP ; +#endif + else + return -EINVAL; t = current;/* Cache in task struct for performance. */ t->kcov_size = kcov->size; t->kcov_area = kcov->area; -/* See comment in __sanitizer_cov_trace_pc(). */ +/* See comment in check_kcov_mode(). */ barrier ();WRITE_ONCE (t->kcov_mode, kcov->mode); t->kcov = kcov;@@ -248,6 +382,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd,return -EINVAL ;kcov_task_init (t); kcov->t =NULL ; + kcov->mode =KCOV_MODE_INIT ;kcov_put (kcov);return 0 ;default : --2.14.1.581.gf28d330327-goog
Quoted 4 lines
--
You received this message because you are subscribed to the Google Groups "syzkaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
--
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>
A message that is not in the archive (sent off-list or not yet received): CAPZ9YJZUPYs8nbwG9aO1uCfr7vPY7PNr1WPpvOxP8d+vkMiDJw@mail.gmail.com
Dmitry Vyukov
reviewerQuoted 172 lines · Andrea Parri
Hi Dmitry,
On Aug 30, 2017 6:26 PM, "Dmitry Vyukov" <[email protected]> wrote:
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_ENABLEioctl call with corresponding argument value.Signed-off-by Victor Chibotaru <[email protected]>Cc Andrew Morton <[email protected]>Cc Mark Rutland <[email protected]>Cc Alexander Popov <[email protected]>Cc Andrey Ryabinin <[email protected]>Cc Kees Cook <[email protected]>Cc Vegard Nossum <[email protected]>Cc Quentin Casasnovas <[email protected]>---
Clang instrumentation:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
Syzkaller:
https://github.com/google/syzkaller--- include/linux/kcov.h | 12 ++- include/uapi/linux/kcov.h | 32 ++++++++ kernel/kcov.c | 203 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 209 insertions(+), 38 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 +20
-0@@ -7,4 +7,36 @@#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 , +}; + +/* +* Defines the format for the types of collected comparisons. +*/ +enum kcov_cmp_type { +/* +* LSB shows whether one of the arguments is a compile-time constant.
include/uapi/linux/kcov.h +4
-0+*/ + KCOV_CMP_CONST = 1, +/* +* Second and third LSBs contain the size of arguments (1/2/4/8 bytes).
include/uapi/linux/kcov.h +8
-0+*/ +KCOV_CMP_SIZE1 =0 , +KCOV_CMP_SIZE2 =2 , +KCOV_CMP_SIZE4 =4 , +KCOV_CMP_SIZE8 =6 , + KCOV_CMP_SIZE_MASK = 6, +}; +#endif /* _LINUX_KCOV_IOCTLS_H */ kernel/kcov.c +9
-1@@ -21,13 +21,21 @@#include <linux/kcov.h> #include <asm/setup.h> +/* Number of words written per one comparison. */ +#define KCOV_WORDS_PER_CMP 3 +/* * kcov descriptor (one per opened debugfs file). * State transitions of the descriptor: * - initial state after open() * - then there must be a single ioctl(KCOV_INIT_TRACE) call * - then, mmap() call (several calls are allowed but not useful) -* - then, repeated enable/disable for a task (only one task a time allowed) +* - then, ioctl(KCOV_ENABLE, arg), where arg is +* KCOV_TRACE_PC - to trace only the PCs +* or +* KCOV_TRACE_CMP - to trace only the comparison operands +* - then, ioctl(KCOV_DISABLE) to disable the task. +* Enabling/disabling ioctls can be repeated (only one task a time allowed).
*/struct kcov { /*kernel/kcov.c +17
-0@@ -47,6 +55,30 @@ struct kcov {struct task_struct *t; }; +static bool check_kcov_mode (enum kcov_mode needed_mode,struct task_struct *t) +{ +enum kcov_mode mode; + +/* +* We are interested in code coverage as a function of a syscall inputs, +* so we ignore code executed in interrupts. +*/ +if (!t || !in_task ()) +return false; + mode =READ_ONCE (t->kcov_mode); +/* +* There is some code that runs in interrupts but for which +* in_interrupt() returns false (e.g. preempt_schedule_irq()). +* READ_ONCE()/barrier() effectively provides load-acquire wrt +* interrupts, there are paired barrier()/WRITE_ONCE() in +* kcov_ioctl_locked().
Quoted 4 lines
Could you elaborate on this: what do you mean by "... provides load-acquire _wrt_ interrupts?" and why so?
Andrea
Without this barrier we can get the following situation. Consider that ioctl sets t->kcov_mode but does not set t->kcov_area yet (and/or __sanitizer_cov_trace_pc reads area ahead of mode), then an interrupt on this task comes but in_task() returns true (observed for preempt_schedule_irq()). Now the interrupt can decide that tracing is enabled and write to area (which is NULL) which will cause panic and crash.
We effectively need an acquire/release pair here, but only between the task and an interrupt running on this task. So we don't need hardware barriers, only compiler ordering.
Quoted 254 lines
kernel/kcov.c +12
-15+*/ +barrier (); +if (mode != needed_mode) +return false; +return true; +} +/* * Entry point from instrumented code. * This is called once per basic-block/edge. @@ -54,44 +86,136 @@ struct kcov { void notrace __sanitizer_cov_trace_pc(void) {struct task_struct *t; -enum kcov_mode mode; +unsigned long *area; +unsigned long ip = _RET_IP_; +unsigned long pos; t = current; -/* -* We are interested in code coverage as a function of a syscall inputs, -* so we ignore code executed in interrupts. -*/ -if (!t || !in_task ()) + if (!check_kcov_mode(KCOV_MODE_TRACE_PC, t)) return; - mode =READ_ONCE (t->kcov_mode); -if (mode ==KCOV_MODE_TRACE ) { -unsigned long *area; -unsigned long pos; -unsigned long ip = _RET_IP_;#ifdef CONFIG_RANDOMIZE_BASE - ip -=kaslr_offset (); + ip -=kaslr_offset ();#endif -/* -* There is some code that runs in interrupts but for which -* in_interrupt() returns false (e.g.
preempt_schedule_irq()).
- * READ_ONCE()/barrier() effectively provides load-acquire
wrtkernel/kcov.c +116
-12-* interrupts, there are paired barrier()/WRITE_ONCE() in -* kcov_ioctl_locked(). -*/ -barrier (); - area = t->kcov_area; -/* The first word is number of subsequent PCs. */ - pos =READ_ONCE (area[0 ]) +1 ; -if (likely (pos < t->kcov_size)) { - area[pos] = ip; -WRITE_ONCE (area[0 ], pos); - } + area = t->kcov_area; +/* The first word is number of subsequent PCs. */ + pos =READ_ONCE (area[0 ]) +1 ; +if (likely (pos < t->kcov_size)) { + area[pos] = ip; +WRITE_ONCE (area[0 ], pos); } }EXPORT_SYMBOL (__sanitizer_cov_trace_pc); +#ifdef CONFIG_KCOV_ENABLE_COMPARISONS +static void write_comp_data (u64 type,u64 arg1,u64 arg2) +{ +struct task_struct *t; +u64 *area; +u64 count, start_index, end_pos, max_pos; + + t = current; +if (!check_kcov_mode (KCOV_MODE_TRACE_CMP , t)) +return ; + +/* +* We write all comparison arguments and types as u64. +* The buffer was allocated for t->kcov_size unsigned longs. +*/ + area = (u64 *)t->kcov_area; + max_pos = t->kcov_size *sizeof (unsigned long ); + + count =READ_ONCE (area[0 ]); + +/* Every record is KCOV_WORDS_PER_CMP words. */ + start_index =1 + count *KCOV_WORDS_PER_CMP ; + end_pos = (start_index +KCOV_WORDS_PER_CMP ) *sizeof (u64); +if (likely (end_pos <= max_pos)) { + area[start_index] = type; + area[start_index +1 ] = arg1; + area[start_index +2 ] = arg2; +WRITE_ONCE (area[0 ], count +1 ); + } +} + +void notrace __sanitizer_cov_trace_cmp1(u8 arg1, u8 arg2) +{ +write_comp_data (KCOV_CMP_SIZE1 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp1); + +void notrace __sanitizer_cov_trace_cmp2(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE2 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp2); + +void notrace __sanitizer_cov_trace_cmp4(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE4 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp4); + +void notrace __sanitizer_cov_trace_cmp8(u64 arg1, u64 arg2) +{ +write_comp_data (KCOV_CMP_SIZE8 , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_cmp8); + +void notrace __sanitizer_cov_trace_const_cmp1(u8 arg1, u8 arg2) +{ +write_comp_data (KCOV_CMP_SIZE1 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp1); + +void notrace __sanitizer_cov_trace_const_cmp2(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE2 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp2); + +void notrace __sanitizer_cov_trace_const_cmp4(u16 arg1, u16 arg2) +{ +write_comp_data (KCOV_CMP_SIZE4 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp4); + +void notrace __sanitizer_cov_trace_const_cmp8(u64 arg1, u64 arg2) +{ +write_comp_data (KCOV_CMP_SIZE8 |KCOV_CMP_CONST , arg1, arg2); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_const_cmp8); + +void notrace __sanitizer_cov_trace_switch(u64 val, u64 *cases) +{ +u64 i; +u64 count = cases[0 ]; +u64 size = cases[1 ]; +u64 type =KCOV_CMP_CONST ; + +switch (size) { +case 8 : + type |=KCOV_CMP_SIZE1 ; +break ; +case 16 : + type |=KCOV_CMP_SIZE2 ; +break ; +case 32 : + type |=KCOV_CMP_SIZE4 ; +break ; +case 64 : + type |=KCOV_CMP_SIZE8 ; +break ; +default : +return ; + } +for (i =0 ; i < count; i++) +write_comp_data (type, cases[i +2 ], val); +} +EXPORT_SYMBOL (__sanitizer_cov_trace_switch); +#endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */ +static void kcov_get (struct kcov *kcov) {atomic_inc (&kcov->refcount);@@ -128,6 +252,7 @@ void kcov_task_exit(struct task_struct *t)/* Just to not leave dangling references behind. */ kcov_task_init (t); kcov->t =NULL ; + kcov->mode =KCOV_MODE_INIT ;spin_unlock (&kcov->lock);kcov_put (kcov); }@@ -146,7 +271,7 @@ static int kcov_mmap(struct file *filep, struct vm_area_struct *vma) spin_lock(&kcov->lock); size = kcov->size *sizeof (unsigned long ); -if (kcov->mode ==KCOV_MODE_DISABLED || vma->vm_pgoff !=0 || +if (kcov->mode !=KCOV_MODE_INIT || vma->vm_pgoff !=0 || vma->vm_end - vma->vm_start != size) { res = -EINVAL ;goto exit;kernel/kcov.c +1
-0@@ -175,6 +300,7 @@ static int kcov_open(struct inode *inode, struct file *filep) kcov =kzalloc (sizeof (*kcov),GFP_KERNEL );if (!kcov)return -ENOMEM ; + kcov->mode =KCOV_MODE_DISABLED ;atomic_set (&kcov->refcount,1 );spin_lock_init (&kcov->lock);filep->private_data = kcov;kernel/kcov.c +1
-1@@ -210,7 +336,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, if (size < 2 || size > INT_MAX / sizeof(unsigned long))return -EINVAL ; kcov->size = size; - kcov->mode =KCOV_MODE_TRACE ; + kcov->mode =KCOV_MODE_INIT ;return 0 ;case KCOV_ENABLE :/*kernel/kcov.c +0
-0@@ -220,17 +346,25 @@ static int kcov_ioctl_locked(struct kcov *kcov,unsigned int cmd, * at task exit or voluntary by KCOV_DISABLE. After that itcan
* be enabled for another task.
*/kernel/kcov.c +13
-4- unused = arg; -if (unused !=0 || kcov->mode ==KCOV_MODE_DISABLED || - kcov->area ==NULL ) +if (kcov->mode !=KCOV_MODE_INIT || !kcov->area)return -EINVAL ;if (kcov->t !=NULL )return -EBUSY ; +if (arg ==KCOV_TRACE_PC ) + kcov->mode =KCOV_MODE_TRACE_PC ; + else if (arg == KCOV_TRACE_CMP) +#ifdef CONFIG_KCOV_ENABLE_COMPARISONS + kcov->mode =KCOV_MODE_TRACE_CMP ; +#else +return -ENOTSUPP ; +#endif + else + return -EINVAL; t = current;/* Cache in task struct for performance. */ t->kcov_size = kcov->size; t->kcov_area = kcov->area; -/* See comment in __sanitizer_cov_trace_pc(). */ +/* See comment in check_kcov_mode(). */ barrier ();WRITE_ONCE (t->kcov_mode, kcov->mode); t->kcov = kcov;@@ -248,6 +382,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsignedint cmd, return -EINVAL;kcov_task_init (t); kcov->t =NULL ; + kcov->mode =KCOV_MODE_INIT ;kcov_put (kcov);return 0 ;
Quoted 5 lines
default:
--
2.14.1.581.gf28d330327-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>
Dmitry Vyukov
reviewerPATCH 2/3 Makefile: support flag -fsanitizer-coverage=trace-cmp
In reply to Dmitry VyukovFrom: 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
--- Makefile | 5 +++-- lib/Kconfig.debug | 8 ++++++++ scripts/Makefile.kcov | 6 ++++++ scripts/Makefile.lib | 6 ++++++ 4 files changed, 23 insertions(+), 2 deletions(-)
Makefile +3 -2
@@ -374,7 +374,7 @@ 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,) - +CFLAGS_KCOV_COMPS := $(call cc-option,-fsanitize-coverage=trace-cmp,) # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE := \
@@ -420,7 +420,7 @@ export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS -export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KASAN CFLAGS_UBSAN +export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KCOV_COMPS CFLAGS_KASAN CFLAGS_UBSAN export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
@@ -822,6 +822,7 @@ KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init) KBUILD_ARFLAGS := $(call ar-option,D) include scripts/Makefile.kasan +include scripts/Makefile.kcov include scripts/Makefile.extrawarn include scripts/Makefile.ubsan
lib/Kconfig.debug +8 -0
@@ -777,6 +777,14 @@ 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 instrumented code. + Note: currently only available if compiled with Clang. + config KCOV_INSTRUMENT_ALL bool "Instrument all code by default" depends on KCOV
scripts/Makefile.kcov new file +6 -0
@@ -0,0 +1,6 @@ +ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) +ifneq ($(cc-name),clang) + $(warning Cannot use CONFIG_KCOV_ENABLE_COMPARISONS: \ + -fsanitize=trace-cmp is not supported by compiler) +endif +endif
scripts/Makefile.lib +6 -0
@@ -142,6 +142,12 @@ _c_flags += $(if $(patsubst n%,, \ $(CFLAGS_KCOV)) endif +ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) +_c_flags += $(if $(patsubst n%,, \ + $(KCOV_INSTRUMENT_$(basetarget).o)$(KCOV_INSTRUMENT)$(CONFIG_KCOV_INSTRUMENT_ALL)), \ + $(CFLAGS_KCOV_COMPS)) +endif + # If building the kernel in a separate objtree expand all occurrences # of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/').
Signature · 7 lines
2.14.1.581.gf28d330327-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>
Dmitry Vyukov
reviewerPATCH 3/3 kcov: update documentation
In reply to Dmitry VyukovFrom: Victor Chibotaru <[email protected]>
The updated documentation describes new KCOV mode for collecting comparison operands.
--- Documentation/dev-tools/kcov.rst | 94 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-)
Documentation/dev-tools/kcov.rst +90 -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,75 @@ 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. */ + + enum kcov_cmp_type { + /* + * LSB shows whether the first argument is a compile-time constant. + */ + KCOV_CMP_CONST = 1, + /* + * Second and third LSBs contain the size of arguments (1/2/4/8 bytes). + */ + KCOV_CMP_SIZE1 = 0, + KCOV_CMP_SIZE2 = 2, + KCOV_CMP_SIZE4 = 4, + KCOV_CMP_SIZE8 = 6, + KCOV_CMP_SIZE_MASK = 6, + }; + + 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*3 + 1]; + /* arg1 and arg2 - operands of the comparison. */ + arg1 = cover[i*3 + 2]; + arg2 = cover[i*3 + 3]; + /* size == KCOV_CMP_SIZEi. */ + size = type & KCOV_CMP_SIZE_MASK; + /* is_const - shows whether arg1 is a compile-time constant.*/ + is_const = type & KCOV_CMP_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.14.1.581.gf28d330327-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>