Module Name: src Committed By: kamil Date: Sat Feb 23 03:10:06 UTC 2019
Modified Files: src/distrib/sets/lists/comp: mi src/distrib/sets/lists/man: mi src/distrib/sets/lists/tests: module.mi src/etc: MAKEDEV.tmpl src/share/man/man4: Makefile src/share/mk: bsd.sys.mk src/sys/arch/amd64/conf: GENERIC src/sys/conf: files majors ssp.mk src/sys/kern: files.kern src/sys/sys: Makefile src/tests/modules: Makefile Added Files: src/share/man/man4: kcov.4 src/sys/kern: subr_kcov.c src/sys/sys: kcov.h src/tests/modules: t_kcov.c Log Message: Add KCOV - kernel code coverage tracing device The KCOV 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 device is oriented towards kernel fuzzers, in particular syzkaller. Currently the only supported coverage type is -fsanitize-coverage=trace-pc. The KCOV driver was initially developed in Linux. A driver based on the same concept was then implemented in FreeBSD and OpenBSD. Documentation is borrowed from OpenBSD and ATF tests from FreeBSD. This patch has been prepared by Siddharth Muralee, improved by <maxv> and polished by myself before importing into the mainline tree. All ATF tests pass. To generate a diff of this commit: cvs rdiff -u -r1.2258 -r1.2259 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.1637 -r1.1638 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.16 -r1.17 src/distrib/sets/lists/tests/module.mi cvs rdiff -u -r1.197 -r1.198 src/etc/MAKEDEV.tmpl cvs rdiff -u -r1.677 -r1.678 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/kcov.4 cvs rdiff -u -r1.290 -r1.291 src/share/mk/bsd.sys.mk cvs rdiff -u -r1.516 -r1.517 src/sys/arch/amd64/conf/GENERIC cvs rdiff -u -r1.1229 -r1.1230 src/sys/conf/files cvs rdiff -u -r1.80 -r1.81 src/sys/conf/majors cvs rdiff -u -r1.3 -r1.4 src/sys/conf/ssp.mk cvs rdiff -u -r1.32 -r1.33 src/sys/kern/files.kern cvs rdiff -u -r0 -r1.1 src/sys/kern/subr_kcov.c cvs rdiff -u -r1.168 -r1.169 src/sys/sys/Makefile cvs rdiff -u -r0 -r1.1 src/sys/sys/kcov.h cvs rdiff -u -r1.14 -r1.15 src/tests/modules/Makefile cvs rdiff -u -r0 -r1.1 src/tests/modules/t_kcov.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.2258 src/distrib/sets/lists/comp/mi:1.2259 --- src/distrib/sets/lists/comp/mi:1.2258 Sun Feb 10 04:03:03 2019 +++ src/distrib/sets/lists/comp/mi Sat Feb 23 03:10:05 2019 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.2258 2019/02/10 04:03:03 mrg Exp $ +# $NetBSD: mi,v 1.2259 2019/02/23 03:10:05 kamil Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. ./etc/mtree/set.comp comp-sys-root @@ -3017,6 +3017,7 @@ ./usr/include/sys/ipc.h comp-c-include ./usr/include/sys/joystick.h comp-c-include ./usr/include/sys/kcore.h comp-c-include +./usr/include/sys/kcov.h comp-c-include ./usr/include/sys/kcpuset.h comp-c-include ./usr/include/sys/kernel.h comp-obsolete obsolete ./usr/include/sys/keylock.h comp-obsolete obsolete Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1637 src/distrib/sets/lists/man/mi:1.1638 --- src/distrib/sets/lists/man/mi:1.1637 Wed Feb 6 11:55:05 2019 +++ src/distrib/sets/lists/man/mi Sat Feb 23 03:10:05 2019 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1637 2019/02/06 11:55:05 rin Exp $ +# $NetBSD: mi,v 1.1638 2019/02/23 03:10:05 kamil Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -1373,6 +1373,7 @@ ./usr/share/man/cat4/jmide.0 man-sys-catman .cat ./usr/share/man/cat4/joy.0 man-sys-catman .cat ./usr/share/man/cat4/kame_ipsec.0 man-obsolete obsolete +./usr/share/man/cat4/kcov.0 man-sys-catman .cat ./usr/share/man/cat4/kloader.0 man-sys-catman .cat ./usr/share/man/cat4/kse.0 man-sys-catman .cat ./usr/share/man/cat4/ksyms.0 man-sys-catman .cat @@ -4496,6 +4497,7 @@ ./usr/share/man/html4/jmide.html man-sys-htmlman html ./usr/share/man/html4/joy.html man-sys-htmlman html ./usr/share/man/html4/kame_ipsec.html man-obsolete obsolete +./usr/share/man/html4/kcov.html man-sys-htmlman html ./usr/share/man/html4/kloader.html man-sys-htmlman html ./usr/share/man/html4/kse.html man-sys-htmlman html ./usr/share/man/html4/ksyms.html man-sys-htmlman html @@ -7459,6 +7461,7 @@ ./usr/share/man/man4/jmide.4 man-sys-man .man ./usr/share/man/man4/joy.4 man-sys-man .man ./usr/share/man/man4/kame_ipsec.4 man-obsolete obsolete +./usr/share/man/man4/kcov.4 man-sys-man .man ./usr/share/man/man4/kloader.4 man-sys-man .man ./usr/share/man/man4/kse.4 man-sys-man .man ./usr/share/man/man4/ksyms.4 man-sys-man .man Index: src/distrib/sets/lists/tests/module.mi diff -u src/distrib/sets/lists/tests/module.mi:1.16 src/distrib/sets/lists/tests/module.mi:1.17 --- src/distrib/sets/lists/tests/module.mi:1.16 Sun Jan 27 16:32:52 2019 +++ src/distrib/sets/lists/tests/module.mi Sat Feb 23 03:10:05 2019 @@ -1,4 +1,4 @@ -# $NetBSD: module.mi,v 1.16 2019/01/27 16:32:52 christos Exp $ +# $NetBSD: module.mi,v 1.17 2019/02/23 03:10:05 kamil Exp $ # # These are only made for ports doing modules. # @@ -17,6 +17,7 @@ ./usr/tests/modules/k_uvm/k_uvm.kmod tests-sys-tests atf,rump ./usr/tests/modules/t_abi_uvm tests-sys-tests atf,rump ./usr/tests/modules/t_builtin tests-sys-tests atf,rump +./usr/tests/modules/t_kcov tests-sys-tests atf ./usr/tests/modules/t_klua_pr_52864 tests-sys-tests atf,rump ./usr/tests/modules/t_modctl tests-sys-tests atf,rump ./usr/tests/modules/t_modload tests-sys-tests atf,rump Index: src/etc/MAKEDEV.tmpl diff -u src/etc/MAKEDEV.tmpl:1.197 src/etc/MAKEDEV.tmpl:1.198 --- src/etc/MAKEDEV.tmpl:1.197 Sun Jan 27 08:53:28 2019 +++ src/etc/MAKEDEV.tmpl Sat Feb 23 03:10:06 2019 @@ -1,5 +1,5 @@ #!/bin/sh - -# $NetBSD: MAKEDEV.tmpl,v 1.197 2019/01/27 08:53:28 maxv Exp $ +# $NetBSD: MAKEDEV.tmpl,v 1.198 2019/02/23 03:10:06 kamil Exp $ # # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -2208,6 +2208,10 @@ autofs) mkdev autofs c %autofs_chr% 0 600 ;; +kcov) + mkdev kcov c %kcov_chr% 0 + ;; + midevend) %MI_DEVICES_END% local) Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.677 src/share/man/man4/Makefile:1.678 --- src/share/man/man4/Makefile:1.677 Wed Feb 6 11:55:05 2019 +++ src/share/man/man4/Makefile Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.677 2019/02/06 11:55:05 rin Exp $ +# $NetBSD: Makefile,v 1.678 2019/02/23 03:10:06 kamil Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -36,7 +36,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a irmce.4 isp.4 ismt.4 isv.4 itesio.4 iteide.4 iwi.4 iwm.4 iwn.4 ixg.4 \ ixpide.4 ixv.4 \ jme.4 jmide.4 joy.4 \ - kloader.4 kse.4 ksyms.4 kttcp.4 \ + kcov.4 kloader.4 kse.4 ksyms.4 kttcp.4 \ l2tp.4 lc.4 ld.4 lii.4 lo.4 lua.4 lxtphy.4 \ mainbus.4 makphy.4 malo.4 mbe.4 mca.4 mcclock.4 md.4 mfb.4 \ mfi.4 mfii.4 mhzc.4 \ Index: src/share/mk/bsd.sys.mk diff -u src/share/mk/bsd.sys.mk:1.290 src/share/mk/bsd.sys.mk:1.291 --- src/share/mk/bsd.sys.mk:1.290 Mon Jan 21 21:11:54 2019 +++ src/share/mk/bsd.sys.mk Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: bsd.sys.mk,v 1.290 2019/01/21 21:11:54 christos Exp $ +# $NetBSD: bsd.sys.mk,v 1.291 2019/02/23 03:10:06 kamil Exp $ # # Build definitions used for NetBSD source tree builds. @@ -240,6 +240,14 @@ KLEAKFLAGS.${f}= # empty CFLAGS+= ${KLEAKFLAGS.${.IMPSRC:T}:U${KLEAKFLAGS}} .endif +.if ${KCOV:U0} > 0 +KCOVFLAGS= -fsanitize-coverage=trace-pc +.for f in subr_kcov.c subr_lwp_specificdata.c subr_specificdata.c +KCOVFLAGS.${f}= # empty +.endfor +CFLAGS+= ${KCOVFLAGS.${.IMPSRC:T}:U${KCOVFLAGS}} +.endif + .if !defined(NOPIE) && (!defined(LDSTATIC) || ${LDSTATIC} != "-static") # Position Independent Executable flags PIE_CFLAGS?= -fPIE Index: src/sys/arch/amd64/conf/GENERIC diff -u src/sys/arch/amd64/conf/GENERIC:1.516 src/sys/arch/amd64/conf/GENERIC:1.517 --- src/sys/arch/amd64/conf/GENERIC:1.516 Fri Feb 15 08:54:01 2019 +++ src/sys/arch/amd64/conf/GENERIC Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.516 2019/02/15 08:54:01 nonaka Exp $ +# $NetBSD: GENERIC,v 1.517 2019/02/23 03:10:06 kamil Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/amd64/conf/std.amd64" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.516 $" +#ident "GENERIC-$Revision: 1.517 $" maxusers 64 # estimated number of users @@ -130,6 +130,10 @@ options KDTRACE_HOOKS # kernel DTrace h #makeoptions KLEAK=1 #options KLEAK +# Kernel Code Coverage Driver. +#makeoptions KCOV=1 +#options KCOV + # Compatibility options # x86_64 never shipped with a.out binaries; the two options below are # only relevant to 32-bit i386 binaries Index: src/sys/conf/files diff -u src/sys/conf/files:1.1229 src/sys/conf/files:1.1230 --- src/sys/conf/files:1.1229 Tue Feb 12 07:16:56 2019 +++ src/sys/conf/files Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1229 2019/02/12 07:16:56 mrg Exp $ +# $NetBSD: files,v 1.1230 2019/02/23 03:10:06 kamil Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20171118 @@ -31,6 +31,7 @@ defflag opt_diagnostic.h _DIAGNOSTIC defflag GPROF defflag KASAN defflag KLEAK +defflag KCOV defparam opt_copy_symtab.h makeoptions_COPY_SYMTAB Index: src/sys/conf/majors diff -u src/sys/conf/majors:1.80 src/sys/conf/majors:1.81 --- src/sys/conf/majors:1.80 Wed Nov 7 07:43:07 2018 +++ src/sys/conf/majors Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: majors,v 1.80 2018/11/07 07:43:07 maxv Exp $ +# $NetBSD: majors,v 1.81 2019/02/23 03:10:06 kamil Exp $ # # Device majors for Machine-Independent drivers. # @@ -79,3 +79,4 @@ device-major qemufwcfg char 342 qemu device-major autofs char 343 autofs device-major gpiopps char 344 gpiopps device-major nvmm char 345 nvmm +device-major kcov char 346 kcov Index: src/sys/conf/ssp.mk diff -u src/sys/conf/ssp.mk:1.3 src/sys/conf/ssp.mk:1.4 --- src/sys/conf/ssp.mk:1.3 Sun Dec 2 21:00:13 2018 +++ src/sys/conf/ssp.mk Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: ssp.mk,v 1.3 2018/12/02 21:00:13 maxv Exp $ +# $NetBSD: ssp.mk,v 1.4 2019/02/23 03:10:06 kamil Exp $ .if ${USE_SSP:Uno} == "yes" COPTS.kern_ssp.c+= -fno-stack-protector -D__SSP__ @@ -11,6 +11,7 @@ COPTS.cpu.c+= -fno-stack-protector .endif COPTS.subr_kleak.c+= -fno-stack-protector +COPTS.subr_kcov.c+= -fno-stack-protector # The following files use alloca(3) or variable array allocations. # Their full name is noted as documentation. Index: src/sys/kern/files.kern diff -u src/sys/kern/files.kern:1.32 src/sys/kern/files.kern:1.33 --- src/sys/kern/files.kern:1.32 Wed Feb 13 18:04:35 2019 +++ src/sys/kern/files.kern Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.kern,v 1.32 2019/02/13 18:04:35 kamil Exp $ +# $NetBSD: files.kern,v 1.33 2019/02/23 03:10:06 kamil Exp $ # # kernel sources @@ -121,6 +121,7 @@ file kern/subr_iostat.c kern file kern/subr_ipi.c kern file kern/subr_kcpuset.c kern file kern/subr_kleak.c kleak +file kern/subr_kcov.c kcov defflag opt_kmem.h KMEM_GUARD KMEM_SIZE defparam opt_kmem.h KMEM_GUARD_DEPTH Index: src/sys/sys/Makefile diff -u src/sys/sys/Makefile:1.168 src/sys/sys/Makefile:1.169 --- src/sys/sys/Makefile:1.168 Tue Aug 21 06:49:21 2018 +++ src/sys/sys/Makefile Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.168 2018/08/21 06:49:21 kre Exp $ +# $NetBSD: Makefile,v 1.169 2019/02/23 03:10:06 kamil Exp $ .include <bsd.own.mk> @@ -25,7 +25,7 @@ INCS= acct.h agpio.h aio.h ansi.h aout_m idtype.h ieee754.h intr.h intrio.h inttypes.h ioccom.h ioctl.h \ ioctl_compat.h iostat.h ipc.h \ joystick.h \ - kcore.h kcpuset.h kgdb.h kmem.h ksem.h ksyms.h ktrace.h \ + kcore.h kcov.h kcpuset.h kgdb.h kmem.h ksem.h ksyms.h ktrace.h \ localcount.h localedef.h lock.h lockf.h lua.h lwp.h lwpctl.h \ malloc.h mallocvar.h mbuf.h md4.h md5.h midiio.h \ mman.h module.h mount.h mqueue.h msg.h msgbuf.h mtio.h mutex.h \ Index: src/tests/modules/Makefile diff -u src/tests/modules/Makefile:1.14 src/tests/modules/Makefile:1.15 --- src/tests/modules/Makefile:1.14 Fri Jan 25 18:33:59 2019 +++ src/tests/modules/Makefile Sat Feb 23 03:10:06 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.14 2019/01/25 18:33:59 christos Exp $ +# $NetBSD: Makefile,v 1.15 2019/02/23 03:10:06 kamil Exp $ .include <bsd.own.mk> @@ -12,6 +12,7 @@ CPPFLAGS+= -D_KERNTYPES # Atffile into it. TESTS_C= t_modctl TESTS_C+= t_builtin +TESTS_C+= t_kcov LDADD= -lprop LDADD+= -lrumpfs_kernfs -lrumpvfs -lrump -lrumpuser -lrump -lpthread Added files: Index: src/share/man/man4/kcov.4 diff -u /dev/null src/share/man/man4/kcov.4:1.1 --- /dev/null Sat Feb 23 03:10:07 2019 +++ src/share/man/man4/kcov.4 Sat Feb 23 03:10:06 2019 @@ -0,0 +1,133 @@ +.\" $NetBSD: kcov.4,v 1.1 2019/02/23 03:10:06 kamil Exp $ +.\" +.\" Copyright (c) 2018 Anton Lindqvist <an...@openbsd.org> +.\" +.\" 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 November 16, 2018 +.Dt KCOV 4 +.Os +.Sh NAME +.Nm kcov +.Nd kernel code coverage tracing +.Sh SYNOPSIS +.Cd options 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 makeoptions KCOV +.Cd options KCOV +to be present, +see +.Xr options 4 . +.Pp +The following +.Xr ioctl 2 +calls are provided: +.Bl -tag -width 4n +.It Dv KCOV_IOC_SETBUFSIZE Fa uint64_t *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 kcov_int_t +entries. Note that kcov_int_t is volatile. +The first entry contains the number of entries in the array, +excluding the first entry. +.It Dv KCOV_IOC_ENABLE Fa void +Enable code coverage tracing for the current thread. +.It Dv KCOV_IOC_DISABLE Fa void +Disable code coverage tracing for the current thread. +.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 <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/ioccom.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include <sys/kcov.h> + +int +main(void) +{ + kcov_int_t *cover, i, n; + kcov_int_t size = 1024 * 100; + int fd; + + fd = open("/dev/kcov", O_RDWR); + if (fd == -1) + err(1, "open"); + if (ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1) + err(1, "ioctl: KCOV_IOC_SETBUFSIZE"); + cover = mmap(NULL, size * KCOV_ENTRY_SIZE, + PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (cover == MAP_FAILED) + err(1, "mmap"); + if (ioctl(fd, KCOV_IOC_ENABLE) == -1) + err(1, "ioctl: KCOV_IOC_ENABLE"); + __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); + read(-1, NULL, 0); /* syscall paths to be traced */ + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + if (ioctl(fd, KCOV_IOC_DISABLE) == -1) + err(1, "ioctl: KCOV_IOC_DISABLE"); + for (i = 0; i < cover[0]; i++) + printf("%p\en", (void *)cover[i + 1]); + if (munmap(cover, size * KCOV_ENTRY_SIZE) == -1) + err(1, "munmap"); + close(fd); + + return 0; +} +.Ed +.Sh SEE ALSO +.Xr options 4 +.Sh HISTORY +The +.Nm +driver was initially developed in Linux. A driver based on the same concept +was then implemented in +.Nx 9 . +.Sh AUTHORS +.An Siddharth Muralee Aq Mt siddharth.mura...@gmail.com Index: src/sys/kern/subr_kcov.c diff -u /dev/null src/sys/kern/subr_kcov.c:1.1 --- /dev/null Sat Feb 23 03:10:07 2019 +++ src/sys/kern/subr_kcov.c Sat Feb 23 03:10:06 2019 @@ -0,0 +1,340 @@ +/* $NetBSD: subr_kcov.c,v 1.1 2019/02/23 03:10:06 kamil Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Siddharth Muralee. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include <sys/cdefs.h> + +#include <sys/module.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <sys/atomic.h> +#include <sys/conf.h> +#include <sys/condvar.h> +#include <sys/kmem.h> +#include <sys/mutex.h> +#include <sys/queue.h> + +#include <uvm/uvm_extern.h> +#include <sys/kcov.h> + +#define KCOV_BUF_MAX_ENTRIES (256 << 10) + +/* + * The KCOV descriptors are allocated during open(), and are associated with + * the calling proc. They are freed lazily when their refcount reaches zero, + * only when the process exits; this guarantees that kd->buf is not mmapped + * in a currently running LWP. A KCOV descriptor is active on only one LWP + * at the same time within the proc. + * + * In the refcount, one ref is for the proc, and one ref is for the LWP where + * the descriptor is active. In each case, the descriptor is pointed to in + * the proc's and LWP's specificdata. + */ + +typedef struct kcov_desc { + kmutex_t lock; + int refcnt; + kcov_int_t *buf; + size_t bufnent; + size_t bufsize; + TAILQ_ENTRY(kcov_desc) entry; +} kcov_t; + +static specificdata_key_t kcov_proc_key; +static specificdata_key_t kcov_lwp_key; + +static void +kcov_lock(kcov_t *kd) +{ + + mutex_enter(&kd->lock); + KASSERT(kd->refcnt > 0); +} + +static void +kcov_unlock(kcov_t *kd) +{ + + mutex_exit(&kd->lock); +} + +static void +kcov_lwp_take(kcov_t *kd) +{ + + kd->refcnt++; + KASSERT(kd->refcnt == 2); + lwp_setspecific(kcov_lwp_key, kd); +} + +static void +kcov_lwp_release(kcov_t *kd) +{ + + KASSERT(kd->refcnt == 2); + kd->refcnt--; + lwp_setspecific(kcov_lwp_key, NULL); +} + +static inline bool +kcov_is_owned(kcov_t *kd) +{ + + return (kd->refcnt > 1); +} + +static void +kcov_free(void *arg) +{ + kcov_t *kd = (kcov_t *)arg; + bool dofree; + + if (kd == NULL) { + return; + } + + kcov_lock(kd); + kd->refcnt--; + kcov_unlock(kd); + dofree = (kd->refcnt == 0); + + if (!dofree) { + return; + } + if (kd->buf != NULL) { + uvm_km_free(kernel_map, (vaddr_t)kd->buf, kd->bufsize, + UVM_KMF_WIRED); + } + mutex_destroy(&kd->lock); + kmem_free(kd, sizeof(*kd)); +} + +static int +kcov_allocbuf(kcov_t *kd, uint64_t nent) +{ + size_t size; + + if (nent < 2 || nent > KCOV_BUF_MAX_ENTRIES) + return EINVAL; + if (kd->buf != NULL) + return EEXIST; + + size = roundup(nent * KCOV_ENTRY_SIZE, PAGE_SIZE); + kd->buf = (kcov_int_t *)uvm_km_alloc(kernel_map, size, 0, + UVM_KMF_WIRED|UVM_KMF_ZERO); + if (kd->buf == NULL) + return ENOMEM; + + kd->bufnent = nent - 1; + kd->bufsize = size; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int +kcov_open(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct proc *p = l->l_proc; + kcov_t *kd; + + kd = proc_getspecific(p, kcov_proc_key); + if (kd != NULL) + return EBUSY; + + kd = kmem_zalloc(sizeof(*kd), KM_SLEEP); + mutex_init(&kd->lock, MUTEX_DEFAULT, IPL_NONE); + kd->refcnt = 1; + proc_setspecific(p, kcov_proc_key, kd); + + return 0; +} + +static int +kcov_close(dev_t dev, int flag, int mode, struct lwp *l) +{ + + return 0; +} + +static int +kcov_ioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) +{ + struct proc *p = l->l_proc; + int error = 0; + kcov_t *kd; + + kd = proc_getspecific(p, kcov_proc_key); + if (kd == NULL) + return ENXIO; + kcov_lock(kd); + + switch (cmd) { + case KCOV_IOC_SETBUFSIZE: + if (kcov_is_owned(kd)) { + error = EBUSY; + break; + } + error = kcov_allocbuf(kd, *((uint64_t *)addr)); + break; + case KCOV_IOC_ENABLE: + if (kcov_is_owned(kd)) { + error = EBUSY; + break; + } + if (kd->buf == NULL) { + error = ENOBUFS; + break; + } + KASSERT(l == curlwp); + kcov_lwp_take(kd); + break; + case KCOV_IOC_DISABLE: + if (lwp_getspecific(kcov_lwp_key) == NULL) { + error = ENOENT; + break; + } + KASSERT(l == curlwp); + kcov_lwp_release(kd); + break; + default: + error = EINVAL; + } + + kcov_unlock(kd); + return error; +} + +static paddr_t +kcov_mmap(dev_t dev, off_t offset, int prot) +{ + kcov_t *kd; + paddr_t pa; + vaddr_t va; + + kd = proc_getspecific(curproc, kcov_proc_key); + KASSERT(kd != NULL); + + if ((offset < 0) || (offset >= kd->bufnent * KCOV_ENTRY_SIZE)) { + return (paddr_t)-1; + } + if (offset & PAGE_MASK) { + return (paddr_t)-1; + } + va = (vaddr_t)kd->buf + offset; + if (!pmap_extract(pmap_kernel(), va, &pa)) { + return (paddr_t)-1; + } + + return atop(pa); +} + +static inline bool +in_interrupt(void) +{ + return curcpu()->ci_idepth >= 0; +} + +void __sanitizer_cov_trace_pc(void); + +void +__sanitizer_cov_trace_pc(void) +{ + extern int cold; + uint64_t idx; + kcov_t *kd; + + if (__predict_false(cold)) { + /* Do not trace during boot. */ + return; + } + + if (in_interrupt()) { + /* Do not trace in interrupts. */ + return; + } + + kd = lwp_getspecific(kcov_lwp_key); + if (__predict_true(kd == NULL)) { + /* Not traced. */ + return; + } + + idx = kd->buf[0]; + if (idx < kd->bufnent) { + kd->buf[idx+1] = (kcov_int_t)__builtin_return_address(0); + kd->buf[0]++; + } +} + +/* -------------------------------------------------------------------------- */ + +const struct cdevsw kcov_cdevsw = { + .d_open = kcov_open, + .d_close = kcov_close, + .d_read = noread, + .d_write = nowrite, + .d_ioctl = kcov_ioctl, + .d_stop = nostop, + .d_tty = notty, + .d_poll = nopoll, + .d_mmap = kcov_mmap, + .d_kqfilter = nokqfilter, + .d_discard = nodiscard, + .d_flag = D_OTHER | D_MPSAFE +}; + +MODULE(MODULE_CLASS_ANY, kcov, NULL); + +static void +kcov_init(void) +{ + + proc_specific_key_create(&kcov_proc_key, kcov_free); + lwp_specific_key_create(&kcov_lwp_key, kcov_free); +} + +static int +kcov_modcmd(modcmd_t cmd, void *arg) +{ + + switch (cmd) { + case MODULE_CMD_INIT: + kcov_init(); + return 0; + case MODULE_CMD_FINI: + return EINVAL; + default: + return ENOTTY; + } +} Index: src/sys/sys/kcov.h diff -u /dev/null src/sys/sys/kcov.h:1.1 --- /dev/null Sat Feb 23 03:10:07 2019 +++ src/sys/sys/kcov.h Sat Feb 23 03:10:06 2019 @@ -0,0 +1,42 @@ +/* $NetBSD: kcov.h,v 1.1 2019/02/23 03:10:06 kamil Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Siddharth Muralee. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _SYS_KCOV_H_ +#define _SYS_KCOV_H_ + +#define KCOV_IOC_SETBUFSIZE _IOW('K', 1, uint64_t) +#define KCOV_IOC_ENABLE _IO('K', 2) +#define KCOV_IOC_DISABLE _IO('K', 3) + +typedef volatile uint64_t kcov_int_t; +#define KCOV_ENTRY_SIZE sizeof(kcov_int_t) + +#endif /* !_SYS_KCOV_H_ */ Index: src/tests/modules/t_kcov.c diff -u /dev/null src/tests/modules/t_kcov.c:1.1 --- /dev/null Sat Feb 23 03:10:07 2019 +++ src/tests/modules/t_kcov.c Sat Feb 23 03:10:06 2019 @@ -0,0 +1,315 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018, 2019 Andrew Turner + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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. + */ +#include <sys/cdefs.h> + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/kcov.h> +#include <sys/mman.h> + +#include <fcntl.h> +#include <pthread.h> +#include <semaphore.h> + +#include <atf-c.h> + +#define PAGE_SIZE sysconf(_SC_PAGESIZE) + +static int +open_kcov(void) +{ + int fd; + + fd = open("/dev/kcov", O_RDWR); + if (fd == -1) + atf_tc_skip("Failed to open /dev/kcov"); + + return fd; +} + +ATF_TC_WITHOUT_HEAD(kcov_bufsize); +ATF_TC_BODY(kcov_bufsize, tc) +{ + int fd; + kcov_int_t size; + fd = open_kcov(); + + size = 0; + ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1); + size = 2; + ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap); +ATF_TC_BODY(kcov_mmap, tc) +{ + void *data; + int fd; + kcov_int_t size = 2 * PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) == MAP_FAILED); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0); + + ATF_REQUIRE((data = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0)) != MAP_FAILED); + + munmap(data, 2 * PAGE_SIZE); + + close(fd); +} + +/* This shouldn't panic */ +ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap); +ATF_TC_BODY(kcov_mmap_no_munmap, tc) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap_no_close); +ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); +} + +static sem_t sem1, sem2; + +static void * +kcov_mmap_enable_thread(void *data) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + *(int *)data = fd; + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == 0); + + sem_post(&sem1); + sem_wait(&sem2); + + return NULL; +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap_enable_thread_close); +ATF_TC_BODY(kcov_mmap_enable_thread_close, tc) +{ + pthread_t thread; + int fd; + + sem_init(&sem1, 0, 0); + sem_init(&sem2, 0, 0); + pthread_create(&thread, NULL, + kcov_mmap_enable_thread, &fd); + sem_wait(&sem1); + close(fd); + sem_post(&sem2); + pthread_join(thread, NULL); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable); +ATF_TC_BODY(kcov_enable, tc) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == -1); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + /* We need to enable before disable */ + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1); + + /* Check enabling works only with a valid trace method */ + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == -1); + + /* Disable should only be called once */ + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1); + + /* Re-enabling should also work */ + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable); +ATF_TC_BODY(kcov_enable_no_disable, tc) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == 0); + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable_no_close); +ATF_TC_BODY(kcov_enable_no_disable_no_close, tc) +{ + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE) == 0); +} + +static void * +common_head(int *fdp) +{ + void *data; + int fd; + kcov_int_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0, + "Unable to set the kcov buffer size"); + + data = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer"); + + *fdp = fd; + return data; +} + +static void +common_tail(int fd, kcov_int_t *data) +{ + + ATF_REQUIRE_MSG(munmap(__UNVOLATILE(data), PAGE_SIZE) == 0, + "Unable to unmap the kcov buffer"); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_basic); +ATF_TC_BODY(kcov_basic, tc) +{ + kcov_int_t *buf; + int fd; + + buf = common_head(&fd); + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE) == 0, + "Unable to enable kcov "); + + __atomic_store_n(&buf[0], 0 , __ATOMIC_RELAXED); + + sleep(0); + ATF_REQUIRE_MSG(__atomic_load_n(&buf[0], __ATOMIC_RELAXED) != 0, "No records found"); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd, buf); +} + +static void * +thread_test_helper(void *ptr) +{ + kcov_int_t *buf = ptr; + + __atomic_store_n(&buf[0], 0, __ATOMIC_RELAXED); + sleep(0); + ATF_REQUIRE_MSG(__atomic_load_n(&buf[0], __ATOMIC_RELAXED) == 0, + "Records changed in blocked thread"); + + return NULL; +} + +ATF_TC_WITHOUT_HEAD(kcov_thread); +ATF_TC_BODY(kcov_thread, tc) +{ + pthread_t thread; + kcov_int_t *buf; + int fd; + + buf = common_head(&fd); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE) == 0, + "Unable to enable kcov "); + + pthread_create(&thread, NULL, thread_test_helper, __UNVOLATILE(buf)); + pthread_join(thread, NULL); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd, buf); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, kcov_bufsize); + ATF_TP_ADD_TC(tp, kcov_mmap); + ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap); + ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close); + ATF_TP_ADD_TC(tp, kcov_enable); + ATF_TP_ADD_TC(tp, kcov_enable_no_disable); + ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close); + ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close); + ATF_TP_ADD_TC(tp, kcov_basic); + ATF_TP_ADD_TC(tp, kcov_thread); + return atf_no_error(); +}