Hi,
This diff add supports for an additional trace mode to kcov where
comparison instructions and switch statements can be traced. The actual
comparison operands ends up in the coverage buffer, with some additional
data. See the manual bits for further info. This mode will generate even
more useful coverage during fuzzing.

The implementation matches both Linux and FreeBSD.

Comments? OK?

Index: share/man/man4/kcov.4
===================================================================
RCS file: /cvs/src/share/man/man4/kcov.4,v
retrieving revision 1.6
diff -u -p -r1.6 kcov.4
--- share/man/man4/kcov.4       27 Dec 2018 19:33:08 -0000      1.6
+++ share/man/man4/kcov.4       16 Jan 2019 20:14:31 -0000
@@ -62,9 +62,35 @@ Enable code coverage tracing for the cur
 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
+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 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.
Index: sys/arch/amd64/conf/Makefile.amd64
===================================================================
RCS file: /cvs/src/sys/arch/amd64/conf/Makefile.amd64,v
retrieving revision 1.108
diff -u -p -r1.108 Makefile.amd64
--- sys/arch/amd64/conf/Makefile.amd64  7 Jan 2019 20:24:59 -0000       1.108
+++ sys/arch/amd64/conf/Makefile.amd64  16 Jan 2019 20:14:31 -0000
@@ -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:
Index: sys/arch/i386/conf/Makefile.i386
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/Makefile.i386,v
retrieving revision 1.130
diff -u -p -r1.130 Makefile.i386
--- sys/arch/i386/conf/Makefile.i386    30 Oct 2018 11:08:30 -0000      1.130
+++ sys/arch/i386/conf/Makefile.i386    16 Jan 2019 20:14:31 -0000
@@ -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:
Index: sys/dev/kcov.c
===================================================================
RCS file: /cvs/src/sys/dev/kcov.c,v
retrieving revision 1.10
diff -u -p -r1.10 kcov.c
--- sys/dev/kcov.c      16 Jan 2019 19:27:07 -0000      1.10
+++ sys/dev/kcov.c      16 Jan 2019 20:14:31 -0000
@@ -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,139 @@ __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;
+
+       /*
+        * A traced comparison requires 4 entries and the first entry is
+        * reserved for the index of the next available entry.
+        */
+       idx = kd->kd_buf[0];
+       if (idx * 4 + 4 + 1 >= kd->kd_nmemb)
+               return;
+
+       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 +309,7 @@ kcovioctl(dev_t dev, u_long cmd, caddr_t
                        break;
                }
                mode = *((int *)data);
-               if (mode != KCOV_MODE_TRACE_PC) {
+               if (mode != KCOV_MODE_TRACE_PC && mode != KCOV_MODE_TRACE_CMP) {
                        error = EINVAL;
                        break;
                }
Index: sys/sys/kcov.h
===================================================================
RCS file: /cvs/src/sys/sys/kcov.h,v
retrieving revision 1.3
diff -u -p -r1.3 kcov.h
--- sys/sys/kcov.h      27 Dec 2018 19:33:08 -0000      1.3
+++ sys/sys/kcov.h      16 Jan 2019 20:14:31 -0000
@@ -27,6 +27,7 @@
 
 #define KCOV_MODE_NONE         0
 #define KCOV_MODE_TRACE_PC     1
+#define KCOV_MODE_TRACE_CMP    2
 
 #ifdef _KERNEL
 

Reply via email to