Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F139378541
D14599.id40301.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D14599.id40301.diff
View Options
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -3783,6 +3783,8 @@
kern/kern_idle.c standard
kern/kern_intr.c standard
kern/kern_jail.c standard
+kern/kern_kcov.c optional kcov \
+ compile-with "${NORMAL_C} -fno-sanitize-coverage=trace-pc"
kern/kern_khelp.c standard
kern/kern_kthread.c standard
kern/kern_ktr.c optional ktr
Index: sys/conf/kern.opts.mk
===================================================================
--- sys/conf/kern.opts.mk
+++ sys/conf/kern.opts.mk
@@ -47,6 +47,7 @@
__DEFAULT_NO_OPTIONS = \
EXTRA_TCP_STACKS \
+ KCOV \
KERNEL_RETPOLINE \
NAND \
OFED \
Index: sys/conf/kern.pre.mk
===================================================================
--- sys/conf/kern.pre.mk
+++ sys/conf/kern.pre.mk
@@ -112,6 +112,10 @@
.endif
DEFINED_PROF= ${PROF}
+.if ${MK_KCOV} != "no"
+CFLAGS+= -fsanitize-coverage=trace-pc
+.endif
+
# Put configuration-specific C flags last (except for ${PROF}) so that they
# can override the others.
CFLAGS+= ${CONF_CFLAGS}
Index: sys/conf/options
===================================================================
--- sys/conf/options
+++ sys/conf/options
@@ -58,6 +58,7 @@
DDB_NUMSYM opt_ddb.h
FULL_BUF_TRACKING opt_global.h
GDB
+KCOV opt_kcov.h
KDB opt_global.h
KDB_TRACE opt_kdb.h
KDB_UNATTENDED opt_kdb.h
Index: sys/kern/kern_kcov.c
===================================================================
--- /dev/null
+++ sys/kern/kern_kcov.c
@@ -0,0 +1,332 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2018 The FreeBSD Foundation. All rights reserved.
+ *
+ * This software was developed by Mitchell Horne under sponsorship of
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/kcov.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <machine/cpufunc.h>
+
+#include <vm/pmap.h>
+
+#define BUF_SIZE_BYTES(info) \
+ (info != NULL ? (size_t)info->size * sizeof(uintptr_t) : 0)
+#define BUF_AVAIL_BYTES(info) \
+ (info != NULL ? (size_t)info->index * sizeof(uintptr_t) : 0)
+
+MALLOC_DEFINE(M_KCOV_INFO, "kcovinfo", "KCOV info type");
+MALLOC_DEFINE(M_KCOV_BUF, "kcovbuffer", "KCOV buffer type");
+
+struct kcov_info {
+ struct sx lock;
+ struct thread *td;
+ uintptr_t *buf;
+ u_int size;
+ u_int index;
+ int mode;
+};
+
+/* Prototypes */
+static d_open_t kcov_open;
+static d_close_t kcov_close;
+static d_read_t kcov_read;
+static d_mmap_t kcov_mmap;
+static d_ioctl_t kcov_ioctl;
+
+static void kcov_info_reset(struct kcov_info *info);
+static int kcov_alloc(struct kcov_info *info, u_int entries);
+static void kcov_init(const void *unused);
+
+static bool kcov_initialized = false;
+
+static struct cdevsw kcov_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = kcov_open,
+ .d_close = kcov_close,
+ .d_read = kcov_read,
+ .d_mmap = kcov_mmap,
+ .d_ioctl = kcov_ioctl,
+ .d_name = "kcov",
+};
+
+static u_int kcov_max_entries = KCOV_MAXENTRIES;
+SYSCTL_UINT(_kern, OID_AUTO, kcov_max_entries, CTLFLAG_RW,
+ &kcov_max_entries, 0,
+ "Maximum number of entries that can be stored in a kcov buffer");
+
+/*
+ * Main entry point. A call to this function will be inserted
+ * at every edge, and if coverage is enabled for the thread
+ * this function will add the PC to the buffer.
+ */
+void
+__sanitizer_cov_trace_pc(void)
+{
+ struct thread *td;
+ struct kcov_info *info;
+
+ /*
+ * To guarantee curthread is properly set, we exit early
+ * until the driver has been initialized
+ */
+ if (!kcov_initialized)
+ return;
+
+ td = curthread;
+ info = td->td_kcov_info;
+
+ /*
+ * Check first that KCOV is enabled for the current thread.
+ * Additionally, we want to exclude (for now) all code that
+ * is not explicitly part of syscall call chain, such as
+ * interrupt handlers, since we are mainly interested in
+ * finding non-trivial paths through the syscall.
+ */
+ if (info == NULL || info->buf == NULL ||
+ info->mode != KCOV_MODE_TRACE_PC ||
+ td->td_intr_nesting_level > 0 || !interrupts_enabled())
+ return;
+
+ if (info->index < info->size) {
+ info->buf[info->index] =
+ (uintptr_t)__builtin_return_address(0);
+ info->index++;
+ }
+}
+
+static int
+kcov_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct kcov_info *info;
+
+ info = malloc(sizeof(struct kcov_info), M_KCOV_INFO,
+ M_ZERO | M_WAITOK);
+ kcov_info_reset(info);
+ sx_init(&info->lock, "kcov_lock");
+ dev->si_drv1 = info;
+
+ return (0);
+}
+
+static int
+kcov_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+ struct kcov_info *info;
+
+ info = dev->si_drv1;
+ if (info == NULL)
+ return (EINVAL);
+
+ td->td_kcov_info = NULL;
+ dev->si_drv1 = NULL;
+ sx_destroy(&info->lock);
+ free(info->buf, M_KCOV_BUF);
+ free(info, M_KCOV_INFO);
+
+ return (0);
+}
+
+static int
+kcov_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct kcov_info *info;
+ size_t len;
+ int error;
+
+ info = dev->si_drv1;
+ if (info == NULL || info->buf == NULL) {
+ return (EINVAL);
+ }
+
+ sx_slock(&info->lock);
+ len = uio->uio_resid <= BUF_AVAIL_BYTES(info) - uio->uio_offset ?
+ uio->uio_resid : BUF_AVAIL_BYTES(info) - uio->uio_offset;
+
+ error = uiomove(info->buf, len, uio);
+ sx_sunlock(&info->lock);
+ return (error);
+}
+
+static int
+kcov_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int prot, vm_memattr_t *memattr __unused)
+{
+ struct kcov_info *info;
+
+ if (prot & PROT_EXEC)
+ return (EINVAL);
+
+ info = dev->si_drv1;
+ if (offset < 0 || offset >= BUF_SIZE_BYTES(info))
+ return (EINVAL);
+
+ *paddr = vtophys(info->buf) + offset;
+ return (0);
+}
+
+static void
+kcov_info_reset(struct kcov_info *info)
+{
+
+ if (info == NULL)
+ return;
+
+ free(info->buf, M_KCOV_BUF);
+ info->buf = NULL;
+ info->mode = KCOV_MODE_NONE;
+ info->size = 0;
+ info->index = 0;
+}
+
+static int
+kcov_alloc(struct kcov_info *info, u_int entries)
+{
+ size_t buf_size;
+
+ if (entries > kcov_max_entries)
+ return (EINVAL);
+
+ /* Align to page size so mmap can't access other kernel memory */
+ buf_size = roundup2(entries, PAGE_SIZE) * sizeof(uintptr_t);
+
+ kcov_info_reset(info);
+ info->buf = malloc(buf_size, M_KCOV_BUF, M_WAITOK);
+ info->size = entries;
+
+ return (0);
+}
+
+static int
+kcov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag __unused,
+ struct thread *td)
+{
+ struct kcov_info *info;
+ int error;
+
+ error = 0;
+ info = dev->si_drv1;
+
+ sx_xlock(&info->lock);
+ switch (cmd) {
+ case KIOSETBUFSIZE:
+ /*
+ * Set the size of the coverage buffer. Should be called
+ * before enabling coverage collection for that thread.
+ */
+ if (info->td != NULL) {
+ error = EBUSY;
+ break;
+ }
+ error = kcov_alloc(info, *(u_int *)data);
+ break;
+ case KIOENABLE:
+ /* Only enable if not currently owned */
+ if (info->td != NULL) {
+ error = EBUSY;
+ break;
+ }
+ info->mode = *(int *)data;
+ td->td_kcov_info = info;
+ info->td = td;
+ break;
+ case KIODISABLE:
+ /* Only the currently enabled thread may disable itself */
+ if (info->td != td) {
+ error = EINVAL;
+ }
+ info->mode = KCOV_MODE_NONE;
+ td->td_kcov_info = NULL;
+ info->td = NULL;
+ break;
+ case KIORESET:
+ info->index = 0;
+ break;
+ case KIONREAD:
+ /* Return the number of entries available to be read */
+ *(u_int *)data = info->index;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ sx_xunlock(&info->lock);
+
+ return (error);
+}
+
+static void
+kcov_init(const void *unused)
+{
+ struct make_dev_args args;
+ struct cdev *dev;
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &kcov_cdevsw;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0660;
+ if (make_dev_s(&args, &dev, "kcov") != 0) {
+ printf("%s", "Failed to create kcov device");
+ return;
+ }
+
+ kcov_initialized = true;
+}
+
+/*
+ * thread_exit() hook
+ */
+void
+kcov_thread_exit(struct thread *td)
+{
+
+ if (td->td_kcov_info != NULL) {
+ td->td_kcov_info->td = NULL;
+ td->td_kcov_info = NULL;
+ }
+}
+
+SYSINIT(kcovdev, SI_SUB_DEVFS, SI_ORDER_ANY, kcov_init, NULL);
Index: sys/kern/kern_thread.c
===================================================================
--- sys/kern/kern_thread.c
+++ sys/kern/kern_thread.c
@@ -30,12 +30,14 @@
#include "opt_witness.h"
#include "opt_hwpmc_hooks.h"
+#include "opt_kcov.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/kcov.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@@ -81,9 +83,9 @@
"struct thread KBI td_flags");
_Static_assert(offsetof(struct thread, td_pflags) == 0x104,
"struct thread KBI td_pflags");
-_Static_assert(offsetof(struct thread, td_frame) == 0x468,
+_Static_assert(offsetof(struct thread, td_frame) == 0x470,
"struct thread KBI td_frame");
-_Static_assert(offsetof(struct thread, td_emuldata) == 0x510,
+_Static_assert(offsetof(struct thread, td_emuldata) == 0x518,
"struct thread KBI td_emuldata");
_Static_assert(offsetof(struct proc, p_flag) == 0xb0,
"struct proc KBI p_flag");
@@ -101,9 +103,9 @@
"struct thread KBI td_flags");
_Static_assert(offsetof(struct thread, td_pflags) == 0xa0,
"struct thread KBI td_pflags");
-_Static_assert(offsetof(struct thread, td_frame) == 0x2e4,
+_Static_assert(offsetof(struct thread, td_frame) == 0x2e8,
"struct thread KBI td_frame");
-_Static_assert(offsetof(struct thread, td_emuldata) == 0x330,
+_Static_assert(offsetof(struct thread, td_emuldata) == 0x334,
"struct thread KBI td_emuldata");
_Static_assert(offsetof(struct proc, p_flag) == 0x68,
"struct proc KBI p_flag");
@@ -532,6 +534,9 @@
SDT_PROBE0(proc, , , lwp__exit);
KASSERT(TAILQ_EMPTY(&td->td_sigqueue.sq_list), ("signal pending"));
+#ifdef KCOV
+ kcov_thread_exit(td);
+#endif
#ifdef AUDIT
AUDIT_SYSCALL_EXIT(0, td);
#endif
Index: sys/sys/kcov.h
===================================================================
--- /dev/null
+++ sys/sys/kcov.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (C) 2018 The FreeBSD Foundation. All rights reserved.
+ *
+ * This software was developed by Mitchell Horne under sponsorship of
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_KCOV_H_
+#define _SYS_KCOV_H_
+
+#include <sys/ioccom.h>
+
+#define KCOV_MAXENTRIES (1 << 24) /* 16M */
+
+#define KCOV_MODE_NONE -1
+#define KCOV_MODE_TRACE_PC 0
+#define KCOV_MODE_TRACE_CMP 1
+
+/* KCOV ioctls */
+#define KIOENABLE _IOW('c', 2, int) /* Enable coverage recording */
+#define KIODISABLE _IO('c', 3) /* Disable coverage recording */
+#define KIOSETBUFSIZE _IOW('c', 4, u_int) /* Set the buffer size */
+#define KIONREAD _IOR('c', 5, u_int) /* Entries available for read */
+#define KIORESET _IO('c', 6) /* Reset the buffer index */
+
+#ifdef _KERNEL
+
+void kcov_thread_exit(struct thread *);
+void __sanitizer_cov_trace_pc(void);
+
+#endif /* _KERNEL */
+#endif /* _SYS_KCOV_H_ */
Index: sys/sys/proc.h
===================================================================
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -176,6 +176,7 @@
struct filemon;
struct kaioinfo;
struct kaudit_record;
+struct kcov_info;
struct kdtrace_proc;
struct kdtrace_thread;
struct mqueue_notifier;
@@ -297,6 +298,7 @@
void *td_su; /* (k) FFS SU private */
sbintime_t td_sleeptimo; /* (t) Sleep timeout. */
int td_rtcgen; /* (s) rtc_generation of abs. sleep */
+ struct kcov_info *td_kcov_info; /* (*) Kernel code coverage data */
#define td_endzero td_sigmask
/* Copied during fork1() or create_thread(). */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Dec 12, 11:23 AM (3 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26895097
Default Alt Text
D14599.id40301.diff (14 KB)
Attached To
Mode
D14599: Implement kernel code coverage (kcov)
Attached
Detach File
Event Timeline
Log In to Comment