From: anton Date: Sun, 19 Aug 2018 11:42:33 +0000 (+0000) Subject: Add kcov(4), a kernel code coverage tracing driver. It's used in conjunction X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=af589a788f87bb883e79b26b36531d73da1037ef;p=openbsd Add kcov(4), a kernel code coverage tracing driver. It's used in conjunction with the syzkaller kernel fuzzer. So far, 8 distinct panics have been found and fixed. This effort will continue. kcov is limited to architectures using Clang as their default compiler and is not enabled by default. With help from mpi@, thanks! ok kettenis@ mpi@ visa@ --- diff --git a/etc/MAKEDEV.common b/etc/MAKEDEV.common index db19b2d4ac0..1631f1b9985 100644 --- a/etc/MAKEDEV.common +++ b/etc/MAKEDEV.common @@ -1,4 +1,4 @@ -vers(a, {-$OpenBSD: MAKEDEV.common,v 1.99 2018/07/28 08:09:50 ratchov Exp $-})dnl +vers(a, {-$OpenBSD: MAKEDEV.common,v 1.100 2018/08/19 11:42:33 anton Exp $-})dnl dnl dnl Copyright (c) 2001-2006 Todd T. Fries dnl @@ -167,6 +167,7 @@ target(all, fuse)dnl target(all, vmm)dnl target(all, pvbus, 0, 1)dnl target(all, bpf)dnl +target(all, kcov)dnl dnl _mkdev(all, {-all-}, {-dnl show_target(all)dnl @@ -521,3 +522,5 @@ _mkdev(vmm, vmm, {-M vmm c major_vmm_c 0 600-})dnl __devitem(pvbus, pvbus*, paravirtual device tree root)dnl _mkdev(pvbus, {-pvbus*-}, {-M pvbus$U c major_pvbus_c $U 640-}, 640)dnl _mkdev(local, local, {-test -s $T.local && sh $T.local-})dnl +__devitem(kcov, kcov, Kernel code coverage tracing)dnl +_mkdev(kcov, kcov, {-M kcov c major_kcov_c 0 600-})dnl diff --git a/etc/etc.amd64/MAKEDEV.md b/etc/etc.amd64/MAKEDEV.md index cd915ec384c..1aa4f673524 100644 --- a/etc/etc.amd64/MAKEDEV.md +++ b/etc/etc.amd64/MAKEDEV.md @@ -1,6 +1,6 @@ define(MACHINE,amd64)dnl vers(__file__, - {-$OpenBSD: MAKEDEV.md,v 1.69 2016/09/11 19:59:53 deraadt Exp $-}, + {-$OpenBSD: MAKEDEV.md,v 1.70 2018/08/19 11:42:33 anton Exp $-}, etc.MACHINE)dnl dnl dnl Copyright (c) 2001-2006 Todd T. Fries @@ -77,6 +77,7 @@ _DEV(hotplug, 82) _DEV(ipmi, 96) dnl _DEV(joy, 26) _DEV(nvram, 85) +_DEV(kcov, 19) _DEV(pci, 72) _DEV(pctr, 46) _DEV(pf, 73) diff --git a/etc/etc.i386/MAKEDEV.md b/etc/etc.i386/MAKEDEV.md index c7f74ee5657..a06b5039ce2 100644 --- a/etc/etc.i386/MAKEDEV.md +++ b/etc/etc.i386/MAKEDEV.md @@ -1,6 +1,6 @@ define(MACHINE,i386)dnl vers(__file__, - {-$OpenBSD: MAKEDEV.md,v 1.83 2016/09/11 19:59:53 deraadt Exp $-}, + {-$OpenBSD: MAKEDEV.md,v 1.84 2018/08/19 11:42:33 anton Exp $-}, etc.MACHINE)dnl dnl dnl Copyright (c) 2001-2006 Todd T. Fries @@ -78,6 +78,7 @@ _DEV(gpr, 80) _DEV(hotplug, 82) _DEV(ipmi, 96) _DEV(joy, 26) +_DEV(kcov, 19) _DEV(nvram, 84) _DEV(pci, 72) _DEV(pctr, 46) diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index d4099ecaa2a..fdb3ca2e130 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.689 2018/08/03 01:50:14 kevlo Exp $ +# $OpenBSD: Makefile,v 1.690 2018/08/19 11:42:33 anton Exp $ MAN= aac.4 ac97.4 acphy.4 acrtc.4 \ acpi.4 acpiac.4 acpials.4 acpiasus.4 acpibat.4 \ @@ -37,7 +37,7 @@ MAN= aac.4 ac97.4 acphy.4 acrtc.4 \ ip.4 ip6.4 ipcomp.4 ipgphy.4 ipmi.4 ips.4 ipsec.4 ipw.4 \ isa.4 isagpio.4 isapnp.4 islrtc.4 it.4 itherm.4 iwi.4 iwn.4 iwm.4 \ ix.4 ixgb.4 jmb.4 jme.4 jmphy.4 \ - kate.4 km.4 ksyms.4 kue.4 lc.4 lge.4 lii.4 lisa.4 lm.4 \ + kate.4 kcov.4 km.4 ksyms.4 kue.4 lc.4 lge.4 lii.4 lisa.4 lm.4 \ lmenv.4 lmn.4 lmtemp.4 lo.4 lpt.4 lxtphy.4 luphy.4 \ maestro.4 mainbus.4 malo.4 maxds.4 maxrtc.4 maxtmp.4 mbg.4 midi.4 \ mii.4 mfi.4 \ diff --git a/share/man/man4/kcov.4 b/share/man/man4/kcov.4 new file mode 100644 index 00000000000..582bc464d52 --- /dev/null +++ b/share/man/man4/kcov.4 @@ -0,0 +1,137 @@ +.\" $OpenBSD: kcov.4,v 1.1 2018/08/19 11:42:33 anton Exp $ +.\" +.\" Copyright (c) 2018 Anton Lindqvist +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: August 19 2018 $ +.Dt KCOV 4 +.Os +.Sh NAME +.Nm kcov +.Nd kernel code coverage tracing +.Sh SYNOPSIS +.Cd option KCOV +.Pp +.In sys/kcov.h +.Sh DESCRIPTION +The +.Nm +driver implements collection of code coverage inside the kernel. +It can be enabled on a per process basis from userland, +allowing the kernel program counter to be collected during syscalls triggered by +the same process. +The collected coverage can be accessed by mapping the device +using +.Xr mmap 2 . +.Pp +By default, +.Nm +is not enabled but requires the compile-time configuration +.Cd option KCOV +to be present, +see +.Xr options 4 . +.Pp +The following +.Xr ioctl 2 +calls are provided: +.Bl -tag -width 4n +.It Dv KIOSETBUFSIZE Fa unsigned long *nentries +Allocate a coverage buffer with a capacity of +.Fa nentries . +The buffer can be accessed using +.Xr mmap 2 +whereas the returned pointer must be interpreted as an array of +.Vt unsigned long +entries. +The first entry contains the number of entries in the array, +excluding the first entry. +.It Dv KIOENABLE Fa void +Enable code coverage tracing for the current process. +.It Dv KIODISABLE Fa void +Disable code coverage tracing for the current process. +.El +.Sh FILES +.Bl -tag -width /dev/kcov -compact +.It Pa /dev/kcov +Default device node. +.El +.Sh EXAMPLES +In the following example, +the +.Xr read 2 +syscall is traced and the coverage displayed which in turn can be passed to +.Xr addr2line 1 +in order to translate the kernel program counter into the file name and line +number it corresponds to. +.Bd -literal +#include +#include +#include + +#include +#include +#include +#include +#include + +int +main(void) +{ + unsigned long *cover, i; + unsigned long size = 1024; + int fd; + + fd = open("/dev/kcov", O_RDWR); + if (fd == -1) + err(1, "open"); + + if (ioctl(fd, KIOSETBUFSIZE, &size) == -1) + err(1, "ioctl: KIOSETBUFSIZE"); + cover = mmap(NULL, size * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (cover == MAP_FAILED) + err(1, "mmap"); + + if (ioctl(fd, KIOENABLE) == -1) + err(1, "ioctl: KIOENABLE"); + read(-1, NULL, 0); + if (ioctl(fd, KIODISABLE) == -1) + err(1, "ioctl: KIODISABLE"); + + for (i = 0; i < cover[0]; i++) + printf("%p\en", (void *)cover[i + 1]); + + if (munmap(cover, size * sizeof(unsigned long)) == -1) + err(1, "munmap"); + close(fd); + + return 0; +} +.Ed +.Sh SEE ALSO +.Xr options 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 6.4 . +.Sh AUTHORS +.An Anton Lindqvist Aq Mt anton@openbsd.org +.Sh CAVEATS +The +.Nm +driver is limited to architectures using +.Xr clang 1 +as their default compiler. diff --git a/sys/arch/amd64/amd64/conf.c b/sys/arch/amd64/amd64/conf.c index 731a1d87f72..b629a95db2b 100644 --- a/sys/arch/amd64/amd64/conf.c +++ b/sys/arch/amd64/amd64/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.60 2016/09/04 10:51:23 naddy Exp $ */ +/* $OpenBSD: conf.c,v 1.61 2018/08/19 11:42:33 anton Exp $ */ /* * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. @@ -112,6 +112,12 @@ int nblkdev = nitems(bdevsw); (dev_type_stop((*))) enodev, 0, seltrue, \ (dev_type_mmap((*))) enodev } +/* open, close, ioctl, mmap */ +#define cdev_kcov_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) enodev, 0, selfalse, \ + (dev_init(c,n,mmap)), 0, D_CLONE } #define mmread mmrw #define mmwrite mmrw @@ -164,6 +170,7 @@ cdev_decl(nvram); cdev_decl(drm); #include "viocon.h" cdev_decl(viocon); +cdev_decl(kcov); #include "wsdisplay.h" #include "wskbd.h" @@ -207,7 +214,7 @@ struct cdevsw cdevsw[] = cdev_lpt_init(NLPT,lpt), /* 16: parallel printer */ cdev_ch_init(NCH,ch), /* 17: SCSI autochanger */ cdev_notdef(), /* 18: was: concatenated disk driver */ - cdev_notdef(), /* 19 */ + cdev_kcov_init(1,kcov), /* 19: kcov */ cdev_uk_init(NUK,uk), /* 20: unknown SCSI */ cdev_notdef(), /* 21 */ cdev_fd_init(1,filedesc), /* 22: file descriptor pseudo-device */ diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index d07eb346fce..d3b593f71f4 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.457 2018/08/03 01:50:14 kevlo Exp $ +# $OpenBSD: GENERIC,v 1.458 2018/08/19 11:42:33 anton Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -658,6 +658,9 @@ pseudo-device hotplug 1 # devices hot plugging # mouse & keyboard multiplexor pseudo-devices pseudo-device wsmux 2 +# kernel code coverage +pseudo-device kcov 1 + # Virtio devices virtio* at pci? # Virtio PCI device vioblk* at virtio? # Virtio block device diff --git a/sys/arch/amd64/conf/Makefile.amd64 b/sys/arch/amd64/conf/Makefile.amd64 index 3950ac592dc..f6f6f286aa9 100644 --- a/sys/arch/amd64/conf/Makefile.amd64 +++ b/sys/arch/amd64/conf/Makefile.amd64 @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.amd64,v 1.98 2018/07/13 08:10:56 deraadt Exp $ +# $OpenBSD: Makefile.amd64,v 1.99 2018/08/19 11:42:33 anton Exp $ # For instructions on building kernels consult the config(8) and options(4) # manual pages. @@ -43,6 +43,10 @@ SORTR= cat .else CMACHFLAGS+= -mretpoline .endif +.if ${IDENT:M-DKCOV} && ${COMPILER_VERSION:Mclang} +CMACHFLAGS+= -fno-ret-protector +PROF= -fsanitize-coverage=trace-pc +.endif .if ${COMPILER_VERSION:Mclang} NO_INTEGR_AS= -no-integrated-as .endif @@ -135,6 +139,12 @@ vers.o: ${SYSTEM_DEP:Ngap.o} sh $S/conf/newvers.sh ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c vers.c +.if ${IDENT:M-DKCOV} && ${COMPILER_VERSION:Mclang} +kcov.o: $S/dev/kcov.c + ${NORMAL_C} -fno-sanitize-coverage=trace-pc +.endif + + clean: rm -f *bsd *bsd.gdb *.[dio] [a-z]*.s assym.* \ gap.link ld.script lorder makegap.sh param.c diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64 index 6da1a7b0aa6..a4b89dde769 100644 --- a/sys/arch/amd64/conf/files.amd64 +++ b/sys/arch/amd64/conf/files.amd64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.amd64,v 1.97 2018/07/12 12:46:00 fcambus Exp $ +# $OpenBSD: files.amd64,v 1.98 2018/08/19 11:42:33 anton Exp $ maxpartitions 16 maxusers 2 16 128 @@ -244,6 +244,12 @@ attach vmm at mainbus file arch/amd64/amd64/vmm.c vmm needs-flag file arch/amd64/amd64/vmm_support.S vmm +# +# KCOV +# +pseudo-device kcov +file dev/kcov.c kcov + # # Machine-independent SD/MMC drivers # diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index 5907f8f4e52..494d6e00d21 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.833 2018/08/03 01:50:14 kevlo Exp $ +# $OpenBSD: GENERIC,v 1.834 2018/08/19 11:42:33 anton Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -764,6 +764,9 @@ pseudo-device hotplug 1 # devices hot plugging # mouse & keyboard multiplexor pseudo-devices pseudo-device wsmux 2 +# kernel code coverage +pseudo-device kcov 1 + # Virtio devices virtio* at pci? # Virtio PCI device vioblk* at virtio? # Virtio block device diff --git a/sys/arch/i386/conf/Makefile.i386 b/sys/arch/i386/conf/Makefile.i386 index 45034af6f22..7ca4991f1f6 100644 --- a/sys/arch/i386/conf/Makefile.i386 +++ b/sys/arch/i386/conf/Makefile.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.i386,v 1.124 2018/07/13 08:10:45 deraadt Exp $ +# $OpenBSD: Makefile.i386,v 1.125 2018/08/19 11:42:33 anton Exp $ # For instructions on building kernels consult the config(8) and options(4) # manual pages. @@ -39,6 +39,9 @@ SORTR= cat .else CMACHFLAGS+= -mretpoline .endif +.if ${IDENT:M-DKCOV} && ${COMPILER_VERSION:Mclang} +PROF= -fsanitize-coverage=trace-pc +.endif .if ${COMPILER_VERSION:Mclang} NO_INTEGR_AS= -no-integrated-as .endif @@ -138,6 +141,11 @@ vers.o: ${SYSTEM_DEP:Ngap.o} sh $S/conf/newvers.sh ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c vers.c +.if ${IDENT:M-DKCOV} && ${COMPILER_VERSION:Mclang} +kcov.o: $S/dev/kcov.c + ${NORMAL_C} -fno-sanitize-coverage=trace-pc +.endif + clean: rm -f *bsd *bsd.gdb *.[dio] [a-z]*.s assym.* \ gap.link ld.script lorder makegap.sh param.c diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index 89017ce54f0..0685b8e8345 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.239 2018/07/09 19:20:29 guenther Exp $ +# $OpenBSD: files.i386,v 1.240 2018/08/19 11:42:33 anton Exp $ # # new style config file for i386 architecture # @@ -398,6 +398,12 @@ attach vmm at mainbus file arch/i386/i386/vmm.c vmm needs-flag file arch/i386/i386/vmm_support.S vmm +# +# KCOV +# +pseudo-device kcov +file dev/kcov.c kcov + # # IPMI # diff --git a/sys/arch/i386/i386/conf.c b/sys/arch/i386/i386/conf.c index 1622e6a90eb..891b3a3a96d 100644 --- a/sys/arch/i386/i386/conf.c +++ b/sys/arch/i386/i386/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.158 2016/10/21 06:20:58 mlarkin Exp $ */ +/* $OpenBSD: conf.c,v 1.159 2018/08/19 11:42:33 anton Exp $ */ /* $NetBSD: conf.c,v 1.75 1996/05/03 19:40:20 christos Exp $ */ /* @@ -114,6 +114,13 @@ int nblkdev = nitems(bdevsw); (dev_type_stop((*))) enodev, 0, seltrue, \ (dev_type_mmap((*))) enodev } +/* open, close, ioctl, mmap */ +#define cdev_kcov_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) enodev, 0, selfalse, \ + (dev_init(c,n,mmap)), 0, D_CLONE } + #define mmread mmrw #define mmwrite mmrw cdev_decl(mm); @@ -165,6 +172,7 @@ cdev_decl(cztty); cdev_decl(nvram); #include "drm.h" cdev_decl(drm); +cdev_decl(kcov); #include "wsdisplay.h" #include "wskbd.h" @@ -211,7 +219,7 @@ struct cdevsw cdevsw[] = cdev_lpt_init(NLPT,lpt), /* 16: parallel printer */ cdev_ch_init(NCH,ch), /* 17: SCSI autochanger */ cdev_notdef(), /* 18: was: concatenated disk driver */ - cdev_notdef(), /* 19 */ + cdev_kcov_init(1,kcov), /* 19: kcov */ cdev_uk_init(NUK,uk), /* 20: unknown SCSI */ cdev_acpiapm_init(1,acpiapm), /* 21: Power Management stuff */ cdev_fd_init(1,filedesc), /* 22: file descriptor pseudo-device */ diff --git a/sys/dev/kcov.c b/sys/dev/kcov.c new file mode 100644 index 00000000000..cc8686a7927 --- /dev/null +++ b/sys/dev/kcov.c @@ -0,0 +1,277 @@ +/* $OpenBSD: kcov.c,v 1.1 2018/08/19 11:42:33 anton Exp $ */ + +/* + * Copyright (c) 2018 Anton Lindqvist + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #define KCOV_DEBUG */ +#ifdef KCOV_DEBUG +#define DPRINTF(x...) do { if (kcov_debug) printf(x); } while (0) +#else +#define DPRINTF(x...) +#endif + +/* kcov descriptor */ +struct kd { + enum { + KCOV_MODE_DISABLED, + KCOV_MODE_INIT, + KCOV_MODE_TRACE_PC, + } kd_mode; + int kd_unit; /* device minor */ + pid_t kd_pid; /* process being traced */ + uintptr_t *kd_buf; /* traced coverage */ + size_t kd_nmemb; + size_t kd_size; + + TAILQ_ENTRY(kd) kd_entry; +}; + +void kcovattach(int); + +int kd_alloc(struct kd *, unsigned long); +struct kd *kd_lookup(int); + +static inline struct kd *kd_lookup_pid(pid_t); +static inline int inintr(void); + +TAILQ_HEAD(, kd) kd_list = TAILQ_HEAD_INITIALIZER(kd_list); + +#ifdef KCOV_DEBUG +int kcov_debug = 1; +#endif + +/* + * Compiling the kernel with the `-fsanitize-coverage=trace-pc' option will + * cause the following function to be called upon function entry and before + * each block instructions that maps to a single line in the original source + * code. + * + * If kcov is enabled for the current process, the executed address will be + * stored in the corresponding coverage buffer. + * The first element in the coverage buffer holds the index of next available + * element. + */ +void +__sanitizer_cov_trace_pc(void) +{ + extern int cold; + struct kd *kd; + uint64_t idx; + + /* Do not trace during boot. */ + if (cold) + return; + + /* Do not trace in interrupts to prevent noisy coverage. */ + if (inintr()) + return; + + kd = kd_lookup_pid(curproc->p_p->ps_pid); + if (kd == NULL) + return; + + idx = kd->kd_buf[0]; + if (idx < kd->kd_nmemb) { + kd->kd_buf[idx + 1] = (uintptr_t)__builtin_return_address(0); + kd->kd_buf[0] = idx + 1; + } +} + +void +kcovattach(int count) +{ +} + +int +kcovopen(dev_t dev, int flag, int mode, struct proc *p) +{ +#ifdef KCOV + struct kd *kd; + + if (kd_lookup(minor(dev)) != NULL) + return (EBUSY); + + DPRINTF("%s: unit=%d\n", __func__, minor(dev)); + + kd = malloc(sizeof(*kd), M_SUBPROC, M_WAITOK | M_ZERO); + kd->kd_unit = minor(dev); + TAILQ_INSERT_TAIL(&kd_list, kd, kd_entry); + return (0); +#else + return (ENXIO); +#endif +} + +int +kcovclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct kd *kd; + + kd = kd_lookup(minor(dev)); + if (kd == NULL) + return (EINVAL); + + DPRINTF("%s: unit=%d\n", __func__, minor(dev)); + + TAILQ_REMOVE(&kd_list, kd, kd_entry); + free(kd->kd_buf, M_SUBPROC, kd->kd_size); + free(kd, M_SUBPROC, sizeof(struct kd)); + return (0); +} + +int +kcovioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct kd *kd; + int error = 0; + + kd = kd_lookup(minor(dev)); + if (kd == NULL) + return (ENXIO); + + switch (cmd) { + case KIOSETBUFSIZE: + if (kd->kd_mode != KCOV_MODE_DISABLED) { + error = EBUSY; + break; + } + error = kd_alloc(kd, *((unsigned long *)data)); + if (error == 0) + kd->kd_mode = KCOV_MODE_INIT; + break; + case KIOENABLE: + if (kd->kd_mode != KCOV_MODE_INIT) { + error = EBUSY; + break; + } + kd->kd_mode = KCOV_MODE_TRACE_PC; + kd->kd_pid = p->p_p->ps_pid; + break; + case KIODISABLE: + /* Only the enabled process may disable itself. */ + if (kd->kd_pid != p->p_p->ps_pid || + kd->kd_mode != KCOV_MODE_TRACE_PC) { + error = EBUSY; + break; + } + kd->kd_mode = KCOV_MODE_INIT; + kd->kd_pid = 0; + break; + default: + error = EINVAL; + DPRINTF("%s: %lu: unknown command\n", __func__, cmd); + } + + DPRINTF("%s: unit=%d, mode=%d, pid=%d, error=%d\n", + __func__, kd->kd_unit, kd->kd_mode, kd->kd_pid, error); + + return (error); +} + +paddr_t +kcovmmap(dev_t dev, off_t offset, int prot) +{ + struct kd *kd; + paddr_t pa; + vaddr_t va; + + kd = kd_lookup(minor(dev)); + if (kd == NULL) + return (paddr_t)(-1); + + if (offset < 0 || offset >= kd->kd_nmemb * sizeof(uintptr_t)) + return (paddr_t)(-1); + + va = (vaddr_t)kd->kd_buf + offset; + if (pmap_extract(pmap_kernel(), va, &pa) == FALSE) + return (paddr_t)(-1); + return (pa); +} + +void +kcov_exit(struct proc *p) +{ + struct kd *kd; + + kd = kd_lookup_pid(p->p_p->ps_pid); + if (kd == NULL) + return; + + kd->kd_mode = KCOV_MODE_INIT; + kd->kd_pid = 0; +} + +struct kd * +kd_lookup(int unit) +{ + struct kd *kd; + + TAILQ_FOREACH(kd, &kd_list, kd_entry) { + if (kd->kd_unit == unit) + return (kd); + } + return (NULL); +} + +int +kd_alloc(struct kd *kd, unsigned long nmemb) +{ + size_t size; + + KASSERT(kd->kd_buf == NULL); + + if (nmemb == 0 || nmemb > KCOV_BUF_MAX_NMEMB) + return (EINVAL); + + size = roundup(nmemb * sizeof(uintptr_t), PAGE_SIZE); + kd->kd_buf = malloc(size, M_SUBPROC, M_WAITOK | M_ZERO); + /* The first element is reserved to hold the number of used elements. */ + kd->kd_nmemb = nmemb - 1; + kd->kd_size = size; + return (0); +} + +static inline struct kd * +kd_lookup_pid(pid_t pid) +{ + struct kd *kd; + + TAILQ_FOREACH(kd, &kd_list, kd_entry) { + if (kd->kd_pid == pid && kd->kd_mode == KCOV_MODE_TRACE_PC) + return (kd); + } + return (NULL); +} + +static inline int +inintr(void) +{ +#if defined(__amd64__) || defined(__i386__) + return (curcpu()->ci_idepth > 0); +#else + return (0); +#endif +} diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 4e1f1dee000..4bacf19e3de 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exit.c,v 1.166 2018/08/13 15:26:17 visa Exp $ */ +/* $OpenBSD: kern_exit.c,v 1.167 2018/08/19 11:42:33 anton Exp $ */ /* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */ /* @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +190,10 @@ exit1(struct proc *p, int rv, int flags) acct_process(p); #endif +#ifdef KCOV + kcov_exit(p); +#endif + #ifdef KTRACE /* release trace file */ if (pr->ps_tracevp) diff --git a/sys/sys/kcov.h b/sys/sys/kcov.h new file mode 100644 index 00000000000..752b290e615 --- /dev/null +++ b/sys/sys/kcov.h @@ -0,0 +1,36 @@ +/* $OpenBSD: kcov.h,v 1.1 2018/08/19 11:42:33 anton Exp $ */ + +/* + * Copyright (c) 2018 Anton Lindqvist + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SYS_KCOV_H_ +#define _SYS_KCOV_H_ + +#include + +#define KIOSETBUFSIZE _IOW('K', 1, unsigned long) +#define KIOENABLE _IO('K', 2) +#define KIODISABLE _IO('K', 3) + +#ifdef _KERNEL + +#define KCOV_BUF_MAX_NMEMB (256 << 10) + +void kcov_exit(struct proc *); + +#endif /* _KERNEL */ + +#endif /* !_SYS_KCOV_H_ */