summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranton <anton@cvs.openbsd.org>2019-01-20 09:57:24 +0000
committeranton <anton@cvs.openbsd.org>2019-01-20 09:57:24 +0000
commita9e6ee043cb1aaaa34286857dc960d0a954de1a8 (patch)
tree168f210604fe67e2a9804e9bd1f890677ffa68c0
parentd158f01c0829fd28fdb02e8086b08ae7b68400ca (diff)
Add support for a new kcov trace mode called KCOV_MODE_TRACE_CMP where
comparison instructions and switch statements are being traced. This mode will be used during fuzzing to generate even more coverage. The same mode is also supported by FreeBSD and Linux. Thanks to jmc@ for improving the manual bits. ok bluhm@ visa@
-rw-r--r--share/man/man4/kcov.432
-rw-r--r--sys/arch/amd64/conf/Makefile.amd646
-rw-r--r--sys/arch/i386/conf/Makefile.i3866
-rw-r--r--sys/dev/kcov.c135
-rw-r--r--sys/sys/kcov.h3
5 files changed, 170 insertions, 12 deletions
diff --git a/share/man/man4/kcov.4 b/share/man/man4/kcov.4
index 74a91607835..1c5097c3884 100644
--- a/share/man/man4/kcov.4
+++ b/share/man/man4/kcov.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: kcov.4,v 1.6 2018/12/27 19:33:08 anton Exp $
+.\" $OpenBSD: kcov.4,v 1.7 2019/01/20 09:57:23 anton Exp $
.\"
.\" Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: December 27 2018 $
+.Dd $Mdocdate: January 20 2019 $
.Dt KCOV 4
.Os
.Sh NAME
@@ -62,9 +62,35 @@ Enable code coverage tracing for the current thread.
The
.Fa mode
must be one of the following:
-.Bl -tag -width KCOV_MODE_TRACE_PC
+.Bl -ohang
.It Dv KCOV_MODE_TRACE_PC
Trace the kernel program counter.
+.It Dv KCOV_MODE_TRACE_CMP
+Trace comparison instructions and switch statements.
+For switch statements, the number of traced comparison instructions is equal to
+the number of switch cases.
+Each traced comparison instruction is represented by 4 entries in the coverage
+buffer:
+.Bl -enum
+.It
+A mask where the least significant bit is set if one of the comparison operands
+is a compile-time constant, which is always true for switch statements.
+The remaining bits represents the log2 size of the operands, ranging from 0 to
+3.
+.It
+First comparison operand.
+For switch statements, this operand corresponds to the case value.
+.It
+Second comparison operand.
+For switch statements, this operand corresponds to the value passed to switch.
+.It
+Kernel program counter where the comparison instruction took place.
+.El
+.Pp
+In this mode, the first entry in the coverage buffer reflects the number of
+traced comparison instructions.
+Thus, the effective number of entries in the coverage buffer is given by
+multiplying the first entry by 4.
.El
.It Dv KIODISABLE Fa void
Disable code coverage tracing for the current thread.
diff --git a/sys/arch/amd64/conf/Makefile.amd64 b/sys/arch/amd64/conf/Makefile.amd64
index cac0cb5535f..eb4a5f10f7c 100644
--- a/sys/arch/amd64/conf/Makefile.amd64
+++ b/sys/arch/amd64/conf/Makefile.amd64
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.amd64,v 1.108 2019/01/07 20:24:59 anton Exp $
+# $OpenBSD: Makefile.amd64,v 1.109 2019/01/20 09:57:23 anton Exp $
# For instructions on building kernels consult the config(8) and options(4)
# manual pages.
@@ -95,7 +95,7 @@ LINKFLAGS+= -S
.endif
.if ${SYSTEM_OBJ:Mkcov.o} && ${COMPILER_VERSION:Mclang}
-PROF= -fsanitize-coverage=trace-pc
+PROF= -fsanitize-coverage=trace-pc,trace-cmp
.endif
%LOAD
@@ -152,7 +152,7 @@ vers.o: ${SYSTEM_DEP:Ngap.o}
.if ${SYSTEM_OBJ:Mkcov.o} && ${COMPILER_VERSION:Mclang}
kcov.o: $S/dev/kcov.c
- ${NORMAL_C} -fno-sanitize-coverage=trace-pc
+ ${NORMAL_C} -fno-sanitize-coverage=trace-pc,trace-cmp
.endif
clean:
diff --git a/sys/arch/i386/conf/Makefile.i386 b/sys/arch/i386/conf/Makefile.i386
index e72bde2cd87..6bfb3a97b0c 100644
--- a/sys/arch/i386/conf/Makefile.i386
+++ b/sys/arch/i386/conf/Makefile.i386
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.i386,v 1.131 2019/01/18 01:34:50 pd Exp $
+# $OpenBSD: Makefile.i386,v 1.132 2019/01/20 09:57:23 anton Exp $
# For instructions on building kernels consult the config(8) and options(4)
# manual pages.
@@ -97,7 +97,7 @@ LINKFLAGS+= -S
.endif
.if ${SYSTEM_OBJ:Mkcov.o} && ${COMPILER_VERSION:Mclang}
-PROF= -fsanitize-coverage=trace-pc
+PROF= -fsanitize-coverage=trace-pc,trace-cmp
.endif
%LOAD
@@ -148,7 +148,7 @@ vers.o: ${SYSTEM_DEP:Ngap.o}
.if ${SYSTEM_OBJ:Mkcov.o} && ${COMPILER_VERSION:Mclang}
kcov.o: $S/dev/kcov.c
- ${NORMAL_C} -fno-sanitize-coverage=trace-pc
+ ${NORMAL_C} -fno-sanitize-coverage=trace-pc,trace-cmp
.endif
clean:
diff --git a/sys/dev/kcov.c b/sys/dev/kcov.c
index 32105910012..f29896d3892 100644
--- a/sys/dev/kcov.c
+++ b/sys/dev/kcov.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kcov.c,v 1.12 2019/01/20 09:47:31 anton Exp $ */
+/* $OpenBSD: kcov.c,v 1.13 2019/01/20 09:57:23 anton Exp $ */
/*
* Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
@@ -26,6 +26,9 @@
#include <uvm/uvm_extern.h>
+#define KCOV_CMP_CONST 0x1
+#define KCOV_CMP_SIZE(x) ((x) << 1)
+
/* #define KCOV_DEBUG */
#ifdef KCOV_DEBUG
#define DPRINTF(x...) do { if (kcov_debug) printf(x); } while (0)
@@ -105,6 +108,134 @@ __sanitizer_cov_trace_pc(void)
}
}
+/*
+ * Compiling the kernel with the `-fsanitize-coverage=trace-cmp' option will
+ * cause the following function to be called upon integer comparisons and switch
+ * statements.
+ *
+ * If kcov is enabled for the current thread, the comparison will be stored in
+ * its corresponding coverage buffer.
+ */
+void
+trace_cmp(uint64_t type, uint64_t arg1, uint64_t arg2, uintptr_t pc)
+{
+ struct kcov_dev *kd;
+ uint64_t idx;
+
+ /*
+ * Do not trace before kcovopen() has been called at least once.
+ * At this point, all secondary CPUs have booted and accessing curcpu()
+ * is safe.
+ */
+ if (kcov_cold)
+ return;
+
+ /* Do not trace in interrupts to prevent noisy coverage. */
+ if (inintr())
+ return;
+
+ kd = curproc->p_kd;
+ if (kd == NULL || kd->kd_mode != KCOV_MODE_TRACE_CMP)
+ return;
+
+ idx = kd->kd_buf[0];
+ if (idx * 4 + 4 <= kd->kd_nmemb) {
+ kd->kd_buf[idx * 4 + 1] = type;
+ kd->kd_buf[idx * 4 + 2] = arg1;
+ kd->kd_buf[idx * 4 + 3] = arg2;
+ kd->kd_buf[idx * 4 + 4] = pc;
+ kd->kd_buf[0] = idx + 1;
+ }
+}
+
+void
+__sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(0), arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(1), arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(2), arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(3), arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
+{
+ trace_cmp(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2,
+ (uintptr_t)__builtin_return_address(0));
+}
+
+void
+__sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
+{
+ uint64_t i, nbits, ncases, type;
+ uintptr_t pc;
+
+ pc = (uintptr_t)__builtin_return_address(0);
+ ncases = cases[0];
+ nbits = cases[1];
+
+ switch (nbits) {
+ case 8:
+ type = KCOV_CMP_SIZE(0);
+ break;
+ case 16:
+ type = KCOV_CMP_SIZE(1);
+ break;
+ case 32:
+ type = KCOV_CMP_SIZE(2);
+ break;
+ case 64:
+ type = KCOV_CMP_SIZE(3);
+ break;
+ default:
+ return;
+ }
+ type |= KCOV_CMP_CONST;
+
+ for (i = 0; i < ncases; i++)
+ trace_cmp(type, cases[i + 2], val, pc);
+}
+
void
kcovattach(int count)
{
@@ -173,7 +304,7 @@ kcovioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
break;
}
mode = *((int *)data);
- if (mode != KCOV_MODE_TRACE_PC) {
+ if (mode != KCOV_MODE_TRACE_PC && mode != KCOV_MODE_TRACE_CMP) {
error = EINVAL;
break;
}
diff --git a/sys/sys/kcov.h b/sys/sys/kcov.h
index 18b5609fa01..717a5e8b936 100644
--- a/sys/sys/kcov.h
+++ b/sys/sys/kcov.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kcov.h,v 1.3 2018/12/27 19:33:08 anton Exp $ */
+/* $OpenBSD: kcov.h,v 1.4 2019/01/20 09:57:23 anton Exp $ */
/*
* Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
@@ -27,6 +27,7 @@
#define KCOV_MODE_NONE 0
#define KCOV_MODE_TRACE_PC 1
+#define KCOV_MODE_TRACE_CMP 2
#ifdef _KERNEL