Module Name:    src
Committed By:   maxv
Date:           Sun Dec  2 21:00:13 UTC 2018

Modified Files:
        src/share/mk: bsd.sys.mk
        src/sys/arch/amd64/conf: GENERIC
        src/sys/arch/amd64/include: param.h
        src/sys/conf: files ssp.mk
        src/sys/kern: files.kern subr_pool.c sys_syscall.c
        src/sys/sys: systm.h
        src/sys/uvm: uvm_km.c
Added Files:
        src/sys/arch/amd64/include: kleak.h
        src/sys/kern: subr_kleak.c
        src/usr.sbin/kleak: Makefile kleak.c

Log Message:
Introduce KLEAK, a new feature that can detect kernel information leaks.

It works by tainting memory sources with marker values, letting the data
travel through the kernel, and scanning the kernel<->user frontier for
these marker values. Combined with compiler instrumentation and rotation
of the markers, it is able to yield relevant results with little effort.

We taint the pools and the stack, and scan copyout/copyoutstr. KLEAK is
supported on amd64 only for now, but it is not complicated to add more
architectures (just a matter of having the address of .text, and a stack
unwinder).

A userland tool is provided, that allows to execute a command in rounds
and monitor the leaks generated all the while.

KLEAK already detected directly 12 kernel info leaks, and prompted changes
that in total fixed 25+ leaks.

Based on an idea developed jointly with Thomas Barabosch (of Fraunhofer
FKIE).


To generate a diff of this commit:
cvs rdiff -u -r1.286 -r1.287 src/share/mk/bsd.sys.mk
cvs rdiff -u -r1.508 -r1.509 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r0 -r1.1 src/sys/arch/amd64/include/kleak.h
cvs rdiff -u -r1.26 -r1.27 src/sys/arch/amd64/include/param.h
cvs rdiff -u -r1.1216 -r1.1217 src/sys/conf/files
cvs rdiff -u -r1.2 -r1.3 src/sys/conf/ssp.mk
cvs rdiff -u -r1.25 -r1.26 src/sys/kern/files.kern
cvs rdiff -u -r0 -r1.1 src/sys/kern/subr_kleak.c
cvs rdiff -u -r1.227 -r1.228 src/sys/kern/subr_pool.c
cvs rdiff -u -r1.11 -r1.12 src/sys/kern/sys_syscall.c
cvs rdiff -u -r1.279 -r1.280 src/sys/sys/systm.h
cvs rdiff -u -r1.145 -r1.146 src/sys/uvm/uvm_km.c
cvs rdiff -u -r0 -r1.1 src/usr.sbin/kleak/Makefile src/usr.sbin/kleak/kleak.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/mk/bsd.sys.mk
diff -u src/share/mk/bsd.sys.mk:1.286 src/share/mk/bsd.sys.mk:1.287
--- src/share/mk/bsd.sys.mk:1.286	Fri Aug  3 02:34:31 2018
+++ src/share/mk/bsd.sys.mk	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: bsd.sys.mk,v 1.286 2018/08/03 02:34:31 kamil Exp $
+#	$NetBSD: bsd.sys.mk,v 1.287 2018/12/02 21:00:13 maxv Exp $
 #
 # Build definitions used for NetBSD source tree builds.
 
@@ -232,6 +232,14 @@ CPUFLAGS+=	-Wa,--fatal-warnings
 CFLAGS+=	${CPUFLAGS}
 AFLAGS+=	${CPUFLAGS}
 
+.if ${KLEAK:U0} > 0
+KLEAKFLAGS=	-fsanitize-coverage=trace-pc
+.for f in subr_kleak.c
+KLEAKFLAGS.${f}=	# empty
+.endfor
+CFLAGS+=	${KLEAKFLAGS.${.IMPSRC:T}:U${KLEAKFLAGS}}
+.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.508 src/sys/arch/amd64/conf/GENERIC:1.509
--- src/sys/arch/amd64/conf/GENERIC:1.508	Sat Nov 24 18:23:29 2018
+++ src/sys/arch/amd64/conf/GENERIC	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.508 2018/11/24 18:23:29 bouyer Exp $
+# $NetBSD: GENERIC,v 1.509 2018/12/02 21:00:13 maxv 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.508 $"
+#ident		"GENERIC-$Revision: 1.509 $"
 
 maxusers	64		# estimated number of users
 
@@ -125,6 +125,10 @@ options 	KDTRACE_HOOKS	# kernel DTrace h
 #options 	KASAN
 #no options	SVS
 
+# Kernel Info Leak Detector.
+#makeoptions 	KLEAK=1
+#options 	KLEAK
+
 # 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/arch/amd64/include/param.h
diff -u src/sys/arch/amd64/include/param.h:1.26 src/sys/arch/amd64/include/param.h:1.27
--- src/sys/arch/amd64/include/param.h:1.26	Wed Aug 22 12:07:43 2018
+++ src/sys/arch/amd64/include/param.h	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: param.h,v 1.26 2018/08/22 12:07:43 maxv Exp $	*/
+/*	$NetBSD: param.h,v 1.27 2018/12/02 21:00:13 maxv Exp $	*/
 
 #ifdef __x86_64__
 
@@ -11,6 +11,7 @@
 #include <machine/cpu.h>
 #if defined(_KERNEL_OPT)
 #include "opt_kasan.h"
+#include "opt_kleak.h"
 #endif
 #endif
 
@@ -61,7 +62,7 @@
 #define	SSIZE		1		/* initial stack size/NBPG */
 #define	SINCR		1		/* increment of stack/NBPG */
 
-#ifdef KASAN
+#if defined(KASAN) || defined(KLEAK)
 #define	UPAGES		8
 #elif defined(DIAGNOSTIC)
 #define	UPAGES		5		/* pages of u-area (1 for redzone) */

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1216 src/sys/conf/files:1.1217
--- src/sys/conf/files:1.1216	Wed Nov  7 07:43:07 2018
+++ src/sys/conf/files	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1216 2018/11/07 07:43:07 maxv Exp $
+#	$NetBSD: files,v 1.1217 2018/12/02 21:00:13 maxv Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20171118
@@ -30,6 +30,7 @@ defparam opt_syslimits.h	CHILD_MAX OPEN_
 defflag opt_diagnostic.h	_DIAGNOSTIC
 defflag				GPROF
 defflag				KASAN
+defflag				KLEAK
 
 defparam opt_copy_symtab.h	makeoptions_COPY_SYMTAB
 

Index: src/sys/conf/ssp.mk
diff -u src/sys/conf/ssp.mk:1.2 src/sys/conf/ssp.mk:1.3
--- src/sys/conf/ssp.mk:1.2	Sun Jan  8 17:10:35 2017
+++ src/sys/conf/ssp.mk	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-# $NetBSD: ssp.mk,v 1.2 2017/01/08 17:10:35 christos Exp $
+# $NetBSD: ssp.mk,v 1.3 2018/12/02 21:00:13 maxv Exp $
 
 .if ${USE_SSP:Uno} == "yes"
 COPTS.kern_ssp.c+=	-fno-stack-protector -D__SSP__
@@ -10,6 +10,8 @@ COPTS.kern_ssp.c+=	-fno-stack-protector 
 COPTS.cpu.c+=		-fno-stack-protector
 .endif
 
+COPTS.subr_kleak.c+=	-fno-stack-protector
+
 # The following files use alloca(3) or variable array allocations.
 # Their full name is noted as documentation.
 VARSTACK= \

Index: src/sys/kern/files.kern
diff -u src/sys/kern/files.kern:1.25 src/sys/kern/files.kern:1.26
--- src/sys/kern/files.kern:1.25	Thu Nov 15 09:38:57 2018
+++ src/sys/kern/files.kern	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files.kern,v 1.25 2018/11/15 09:38:57 maxv Exp $
+#	$NetBSD: files.kern,v 1.26 2018/12/02 21:00:13 maxv Exp $
 
 #
 # kernel sources
@@ -117,6 +117,7 @@ file	kern/subr_interrupt.c		kern
 file	kern/subr_iostat.c		kern
 file	kern/subr_ipi.c			kern
 file	kern/subr_kcpuset.c		kern
+file	kern/subr_kleak.c		kleak
 defflag	opt_kmem.h			KMEM_GUARD
 					KMEM_SIZE
 defparam opt_kmem.h			KMEM_GUARD_DEPTH

Index: src/sys/kern/subr_pool.c
diff -u src/sys/kern/subr_pool.c:1.227 src/sys/kern/subr_pool.c:1.228
--- src/sys/kern/subr_pool.c:1.227	Mon Sep 10 13:11:05 2018
+++ src/sys/kern/subr_pool.c	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_pool.c,v 1.227 2018/09/10 13:11:05 maxv Exp $	*/
+/*	$NetBSD: subr_pool.c,v 1.228 2018/12/02 21:00:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1999, 2000, 2002, 2007, 2008, 2010, 2014, 2015
@@ -33,11 +33,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.227 2018/09/10 13:11:05 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.228 2018/12/02 21:00:13 maxv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
 #include "opt_lockdebug.h"
+#include "opt_kleak.h"
 #endif
 
 #include <sys/param.h>
@@ -105,6 +106,14 @@ static void pool_redzone_check(struct po
 # define pool_redzone_check(pp, ptr)	/* NOTHING */
 #endif
 
+#ifdef KLEAK
+static void pool_kleak_fill(struct pool *, void *);
+static void pool_cache_kleak_fill(pool_cache_t, void *);
+#else
+#define pool_kleak_fill(pp, ptr)	__nothing
+#define pool_cache_kleak_fill(pc, ptr)	__nothing
+#endif
+
 static void *pool_page_alloc_meta(struct pool *, int);
 static void pool_page_free_meta(struct pool *, void *);
 
@@ -937,6 +946,7 @@ pool_get(struct pool *pp, int flags)
 	KASSERT((((vaddr_t)v + pp->pr_itemoffset) & (pp->pr_align - 1)) == 0);
 	FREECHECK_OUT(&pp->pr_freecheck, v);
 	pool_redzone_fill(pp, v);
+	pool_kleak_fill(pp, v);
 	return (v);
 }
 
@@ -2256,6 +2266,7 @@ pool_cache_get_slow(pool_cache_cpu_t *cc
 
 	FREECHECK_OUT(&pc->pc_freecheck, object);
 	pool_redzone_fill(&pc->pc_pool, object);
+	pool_cache_kleak_fill(pc, object);
 	return false;
 }
 
@@ -2303,6 +2314,7 @@ pool_cache_get_paddr(pool_cache_t pc, in
 			splx(s);
 			FREECHECK_OUT(&pc->pc_freecheck, object);
 			pool_redzone_fill(&pc->pc_pool, object);
+			pool_cache_kleak_fill(pc, object);
 			return object;
 		}
 
@@ -2728,6 +2740,26 @@ pool_page_free_meta(struct pool *pp, voi
 	vmem_free(kmem_meta_arena, (vmem_addr_t)v, pp->pr_alloc->pa_pagesz);
 }
 
+#ifdef KLEAK
+static void
+pool_kleak_fill(struct pool *pp, void *p)
+{
+	if (__predict_false(pp->pr_roflags & PR_NOTOUCH)) {
+		return;
+	}
+	kleak_fill_area(p, pp->pr_size);
+}
+
+static void
+pool_cache_kleak_fill(pool_cache_t pc, void *p)
+{
+	if (__predict_false(pc->pc_ctor != NULL || pc->pc_dtor != NULL)) {
+		return;
+	}
+	pool_kleak_fill(&pc->pc_pool, p);
+}
+#endif
+
 #ifdef POOL_REDZONE
 #if defined(_LP64)
 # define PRIME 0x9e37fffffffc0000UL

Index: src/sys/kern/sys_syscall.c
diff -u src/sys/kern/sys_syscall.c:1.11 src/sys/kern/sys_syscall.c:1.12
--- src/sys/kern/sys_syscall.c:1.11	Sat Mar  7 16:38:49 2015
+++ src/sys/kern/sys_syscall.c	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_syscall.c,v 1.11 2015/03/07 16:38:49 christos Exp $	*/
+/*	$NetBSD: sys_syscall.c,v 1.12 2018/12/02 21:00:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2006 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_syscall.c,v 1.11 2015/03/07 16:38:49 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_syscall.c,v 1.12 2018/12/02 21:00:13 maxv Exp $");
 
 #include <sys/syscall_stats.h>
 #include <sys/syscallvar.h>
@@ -85,6 +85,7 @@ SYS_SYSCALL(struct lwp *l, const struct 
 	error = trace_enter(code, callp, TRACE_ARGS);
 	if (__predict_false(error != 0))
 		return error;
+	kleak_fill_stack();
 	error = sy_call(callp, l, &uap->args, rval);
 	trace_exit(code, callp, &uap->args, rval, error);
 	return error;

Index: src/sys/sys/systm.h
diff -u src/sys/sys/systm.h:1.279 src/sys/sys/systm.h:1.280
--- src/sys/sys/systm.h:1.279	Fri Oct  5 22:12:37 2018
+++ src/sys/sys/systm.h	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: systm.h,v 1.279 2018/10/05 22:12:37 christos Exp $	*/
+/*	$NetBSD: systm.h,v 1.280 2018/12/02 21:00:13 maxv Exp $	*/
 
 /*-
  * Copyright (c) 1982, 1988, 1991, 1993
@@ -43,6 +43,7 @@
 #include "opt_ddb.h"
 #include "opt_multiprocessor.h"
 #include "opt_gprof.h"
+#include "opt_kleak.h"
 #endif
 #if !defined(_KERNEL) && !defined(_STANDALONE)
 #include <stdbool.h>
@@ -269,6 +270,18 @@ int	copyoutstr(const void *, void *, siz
 int	copyin(const void *, void *, size_t);
 int	copyout(const void *, void *, size_t);
 
+#ifdef KLEAK
+#define copyout		kleak_copyout
+#define copyoutstr	kleak_copyoutstr
+int	kleak_copyout(const void *, void *, size_t);
+int	kleak_copyoutstr(const void *, void *, size_t, size_t *);
+void	kleak_fill_area(void *, size_t);
+void	kleak_fill_stack(void);
+#else
+#define kleak_fill_area(a, b)	__nothing
+#define kleak_fill_stack()	__nothing
+#endif
+
 #ifdef _KERNEL
 typedef	int	(*copyin_t)(const void *, void *, size_t);
 typedef int	(*copyout_t)(const void *, void *, size_t);

Index: src/sys/uvm/uvm_km.c
diff -u src/sys/uvm/uvm_km.c:1.145 src/sys/uvm/uvm_km.c:1.146
--- src/sys/uvm/uvm_km.c:1.145	Sun Nov  4 13:48:27 2018
+++ src/sys/uvm/uvm_km.c	Sun Dec  2 21:00:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_km.c,v 1.145 2018/11/04 13:48:27 mlelstv Exp $	*/
+/*	$NetBSD: uvm_km.c,v 1.146 2018/12/02 21:00:13 maxv Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -152,7 +152,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_km.c,v 1.145 2018/11/04 13:48:27 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_km.c,v 1.146 2018/12/02 21:00:13 maxv Exp $");
 
 #include "opt_uvmhist.h"
 
@@ -701,6 +701,10 @@ uvm_km_alloc(struct vm_map *map, vsize_t
 
 	pmap_update(pmap_kernel());
 
+	if ((flags & UVM_KMF_ZERO) == 0) {
+		kleak_fill_area((void *)kva, size);
+	}
+
 	UVMHIST_LOG(maphist,"<- done (kva=0x%jx)", kva,0,0,0);
 	return(kva);
 }

Added files:

Index: src/sys/arch/amd64/include/kleak.h
diff -u /dev/null src/sys/arch/amd64/include/kleak.h:1.1
--- /dev/null	Sun Dec  2 21:00:14 2018
+++ src/sys/arch/amd64/include/kleak.h	Sun Dec  2 21:00:13 2018
@@ -0,0 +1,101 @@
+/*	$NetBSD: kleak.h,v 1.1 2018/12/02 21:00:13 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * 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/ksyms.h>
+
+#include <amd64/pmap.h>
+#include <amd64/vmparam.h>
+
+static void
+kleak_md_init(uintptr_t *sva, uintptr_t *eva)
+{
+	extern char __rodata_start;
+	*sva = (uintptr_t)KERNTEXTOFF;
+	*eva = (uintptr_t)&__rodata_start;
+}
+
+static inline bool
+__md_unwind_end(const char *name)
+{
+	if (!strcmp(name, "syscall") ||
+	    !strcmp(name, "handle_syscall") ||
+	    !strncmp(name, "Xintr", 5) ||
+	    !strncmp(name, "Xhandle", 7) ||
+	    !strncmp(name, "Xresume", 7) ||
+	    !strncmp(name, "Xstray", 6) ||
+	    !strncmp(name, "Xhold", 5) ||
+	    !strncmp(name, "Xrecurse", 8) ||
+	    !strcmp(name, "Xdoreti") ||
+	    !strncmp(name, "Xsoft", 5)) {
+		return true;
+	}
+
+	return false;
+}
+
+static void
+kleak_md_unwind(struct kleak_hit *hit)
+{
+	uint64_t *rbp, rip;
+	const char *mod;
+	const char *sym;
+	int error;
+
+	rbp = (uint64_t *)__builtin_frame_address(0);
+
+	hit->npc = 0;
+
+	while (1) {
+		/* 8(%rbp) contains the saved %rip. */
+		rip = *(rbp + 1);
+
+		if (rip < KERNBASE) {
+			break;
+		}
+		error = ksyms_getname(&mod, &sym, (vaddr_t)rip, KSYMS_PROC);
+		if (error) {
+			break;
+		}
+		hit->pc[hit->npc++] = rip;
+		if (__md_unwind_end(sym)) {
+			break;
+		}
+
+		rbp = (uint64_t *)*(rbp);
+		if (rbp == 0) {
+			break;
+		}
+
+		if (hit->npc >= KLEAK_HIT_MAXPC) {
+			break;
+		}
+	}
+}

Index: src/sys/kern/subr_kleak.c
diff -u /dev/null src/sys/kern/subr_kleak.c:1.1
--- /dev/null	Sun Dec  2 21:00:14 2018
+++ src/sys/kern/subr_kleak.c	Sun Dec  2 21:00:13 2018
@@ -0,0 +1,440 @@
+/*	$NetBSD: subr_kleak.c,v 1.1 2018/12/02 21:00:13 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard. Based on an idea developed by Maxime Villard and
+ * Thomas Barabosch of Fraunhofer FKIE.
+ *
+ * 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>
+__KERNEL_RCSID(0, "$NetBSD: subr_kleak.c,v 1.1 2018/12/02 21:00:13 maxv Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <sys/ksyms.h>
+#include <sys/callout.h>
+
+#define __RET_ADDR	__builtin_return_address(0)
+
+#undef copyout
+#undef copyoutstr
+
+static uintptr_t kleak_kernel_text __read_mostly;
+
+static bool kleak_enabled = false;
+static int kleak_nrounds = 1;	/* tunable [1:8] */
+
+static bool dummy1, dummy2, dummy3;
+
+static kmutex_t kleak_mtx __cacheline_aligned;
+
+static uint8_t *kleak_buf;
+static size_t kleak_buf_size;
+
+static uint8_t kleak_pattern_list[8] = {
+	154, 157, 221, 218, 159, 169, 167, 181
+};
+static size_t kleak_index;
+static volatile uint8_t kleak_pattern_byte;
+
+/* -------------------------------------------------------------------------- */
+
+#define KLEAK_HIT_MAXPC		16
+#define KLEAK_RESULT_MAXHIT	32
+
+struct kleak_hit {
+	size_t len;
+	size_t leaked;
+	size_t npc;
+	uintptr_t pc[KLEAK_HIT_MAXPC];
+};
+
+struct kleak_result {
+	size_t nhits;
+	size_t nmiss;
+	struct kleak_hit hits[KLEAK_RESULT_MAXHIT];
+};
+
+static struct kleak_result result;
+
+/* The MD code. */
+#include <machine/kleak.h>
+
+/* -------------------------------------------------------------------------- */
+
+static void
+kleak_note(const void *pc, size_t len, size_t leaked)
+{
+	struct kleak_hit *hit;
+	uint8_t *byte;
+	size_t i, off;
+
+	if ((uintptr_t)pc < kleak_kernel_text) {
+		return;
+	}
+	off = ((uintptr_t)pc - kleak_kernel_text);
+	if (__predict_false(off >= kleak_buf_size)) {
+		return;
+	}
+
+	byte = kleak_buf + off;
+
+	mutex_enter(&kleak_mtx);
+
+	*byte |= __BIT(kleak_index);
+	for (i = 0; i < kleak_nrounds; i++) {
+		if (__predict_true((*byte & __BIT(i)) == 0))
+			goto out;
+	}
+
+	*byte = 0;
+
+	if (__predict_false(result.nhits == KLEAK_RESULT_MAXHIT)) {
+		result.nmiss++;
+		goto out;
+	}
+
+	hit = &result.hits[result.nhits++];
+	hit->len = len;
+	hit->leaked = leaked;
+	kleak_md_unwind(hit);
+
+out:
+	mutex_exit(&kleak_mtx);
+}
+
+int
+kleak_copyout(const void *kaddr, void *uaddr, size_t len)
+{
+	const uint8_t *ptr = (const uint8_t *)kaddr;
+	size_t remain = len;
+	size_t cnt = 0;
+
+	if (!kleak_enabled) {
+		goto out;
+	}
+
+	while (remain-- > 0) {
+		if (__predict_false(*ptr == kleak_pattern_byte)) {
+			cnt++;
+		}
+		ptr++;
+	}
+
+	if (__predict_false(cnt > 0)) {
+		kleak_note(__RET_ADDR, len, cnt);
+	}
+
+out:
+	return copyout(kaddr, uaddr, len);
+}
+
+int
+kleak_copyoutstr(const void *kaddr, void *uaddr, size_t len, size_t *done)
+{
+	const uint8_t *ptr = (const uint8_t *)kaddr;
+	size_t remain = len;
+	size_t cnt = 0;
+
+	if (!kleak_enabled) {
+		goto out;
+	}
+
+	while (remain-- > 0) {
+		if (*ptr == '\0') {
+			break;
+		}
+		if (__predict_false(*ptr == kleak_pattern_byte)) {
+			cnt++;
+		}
+		ptr++;
+	}
+
+	if (__predict_false(cnt > 0)) {
+		kleak_note(__RET_ADDR, len, cnt);
+	}
+
+out:
+	return copyoutstr(kaddr, uaddr, len, done);
+}
+
+void
+kleak_fill_area(void *ptr, size_t len)
+{
+	memset(ptr, kleak_pattern_byte, len);
+}
+
+void
+kleak_fill_stack(void)
+{
+	char buf[USPACE-(2*PAGE_SIZE)];
+	explicit_memset(buf, kleak_pattern_byte, sizeof(buf));
+}
+
+void __sanitizer_cov_trace_pc(void);
+
+/*
+ * We want an explicit memset, but inlined. So use a builtin with optimization
+ * disabled.
+ */
+void __attribute__((optimize("O0")))
+__sanitizer_cov_trace_pc(void)
+{
+	char buf[512];
+	__builtin_memset(buf, kleak_pattern_byte, sizeof(buf));
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void
+kleak_init(void)
+{
+	uintptr_t sva, eva;
+
+	kleak_md_init(&sva, &eva);
+	kleak_kernel_text = sva;
+
+	kleak_index = 0;
+	kleak_pattern_byte = kleak_pattern_list[kleak_index];
+
+	if (kleak_buf == NULL) {
+		mutex_init(&kleak_mtx, MUTEX_DEFAULT, IPL_NONE);
+		kleak_buf_size = (size_t)eva - (size_t)sva;
+		kleak_buf = kmem_zalloc(kleak_buf_size, KM_SLEEP);
+	} else {
+		/* Already initialized, just reset. */
+		mutex_enter(&kleak_mtx);
+		memset(kleak_buf, 0, kleak_buf_size);
+		mutex_exit(&kleak_mtx);
+	}
+}
+
+static int
+kleak_rotate(void)
+{
+	mutex_enter(&kleak_mtx);
+	kleak_index++;
+	mutex_exit(&kleak_mtx);
+
+	/* XXX: Should be atomic. */
+	kleak_pattern_byte = kleak_pattern_list[kleak_index];
+
+	if (kleak_index >= kleak_nrounds) {
+		return ENOENT;
+	}
+
+	return 0;
+}
+
+static int
+sysctl_kleak_rounds(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int error, val;
+
+	val = *(int *)rnode->sysctl_data;
+
+	node = *rnode;
+	node.sysctl_data = &val;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error != 0 || newp == NULL)
+		return error;
+	if (val < 1 || val > 8)
+		return EINVAL;
+	if (kleak_enabled)
+		return EINVAL;
+
+	*(int *)rnode->sysctl_data = val;
+
+	return 0;
+}
+
+static int
+sysctl_kleak_patterns(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	uint8_t val[8];
+	int error;
+
+	memcpy(val, rnode->sysctl_data, sizeof(val));
+
+	node = *rnode;
+	node.sysctl_data = &val;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error != 0 || newp == NULL)
+		return error;
+	if (kleak_enabled)
+		return EINVAL;
+
+	memcpy(rnode->sysctl_data, val, 8 * sizeof(uint8_t));
+
+	return 0;
+}
+
+static int
+sysctl_kleak_start(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int error;
+	bool val;
+
+	val = *(bool *)rnode->sysctl_data;
+
+	node = *rnode;
+	node.sysctl_data = &val;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error != 0 || newp == NULL)
+		return error;
+	if (kleak_enabled)
+		return EEXIST;
+	if (!val)
+		return EINVAL;
+
+	kleak_init();
+	memset(&result, 0, sizeof(result));
+	kleak_enabled = true;
+
+	return 0;
+}
+
+static int
+sysctl_kleak_rotate(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int error;
+	bool val;
+
+	val = *(bool *)rnode->sysctl_data;
+
+	node = *rnode;
+	node.sysctl_data = &val;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error != 0 || newp == NULL)
+		return error;
+	if (!val)
+		return EINVAL;
+
+	return kleak_rotate();
+}
+
+static int
+sysctl_kleak_stop(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int error;
+	bool val;
+
+	val = *(bool *)rnode->sysctl_data;
+
+	node = *rnode;
+	node.sysctl_data = &val;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error != 0 || newp == NULL)
+		return error;
+	if (!val)
+		return EINVAL;
+
+	kleak_enabled = false;
+
+	return 0;
+}
+
+static int
+sysctl_kleak_result(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+
+	node = *rnode;
+	node.sysctl_data = &result;
+	node.sysctl_size = sizeof(result);
+
+	return sysctl_lookup(SYSCTLFN_CALL(&node));
+}
+
+SYSCTL_SETUP(sysctl_kleak_setup, "sysctl kleak subtree setup")
+{
+	const struct sysctlnode *kleak_rnode;
+
+	kleak_rnode = NULL;
+
+	sysctl_createv(clog, 0, NULL, &kleak_rnode,
+		       CTLFLAG_PERMANENT,
+		       CTLTYPE_NODE, "kleak", NULL,
+		       NULL, 0, NULL, 0,
+		       CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_READWRITE,
+		       CTLTYPE_INT, "rounds",
+		       SYSCTL_DESCR("Number of rounds"),
+		       sysctl_kleak_rounds, 0, &kleak_nrounds, 0,
+		       CTL_CREATE, CTL_EOL);
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_READWRITE,
+		       CTLTYPE_STRUCT, "patterns",
+		       SYSCTL_DESCR("List of patterns"),
+		       sysctl_kleak_patterns, 0, &kleak_pattern_list,
+	               sizeof(kleak_pattern_list),
+		       CTL_CREATE, CTL_EOL);
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_READWRITE,
+		       CTLTYPE_BOOL, "start",
+		       SYSCTL_DESCR("Start KLEAK"),
+		       sysctl_kleak_start, 0, &dummy1, 0,
+		       CTL_CREATE, CTL_EOL);
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_READWRITE,
+		       CTLTYPE_BOOL, "rotate",
+		       SYSCTL_DESCR("Rotate the pattern"),
+		       sysctl_kleak_rotate, 0, &dummy2, 0,
+		       CTL_CREATE, CTL_EOL);
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_READWRITE,
+		       CTLTYPE_BOOL, "stop",
+		       SYSCTL_DESCR("Stop KLEAK"),
+		       sysctl_kleak_stop, 0, &dummy3, 0,
+		       CTL_CREATE, CTL_EOL);
+	sysctl_createv(clog, 0, &kleak_rnode, NULL,
+		       CTLFLAG_PERMANENT,
+		       CTLTYPE_STRUCT, "result",
+		       SYSCTL_DESCR("Get the result"),
+		       sysctl_kleak_result, 0, NULL, 0,
+		       CTL_CREATE, CTL_EOL);
+}

Index: src/usr.sbin/kleak/Makefile
diff -u /dev/null src/usr.sbin/kleak/Makefile:1.1
--- /dev/null	Sun Dec  2 21:00:14 2018
+++ src/usr.sbin/kleak/Makefile	Sun Dec  2 21:00:13 2018
@@ -0,0 +1,14 @@
+#	$NetBSD: Makefile,v 1.1 2018/12/02 21:00:13 maxv Exp $
+
+NOMAN=		# defined
+
+PROG=	kleak
+SRCS=	kleak.c
+
+LDADD+= -lelf
+LDADD+= -lutil
+DPADD+= ${LIBELF}
+DPADD+= ${LIBUTIL}
+
+.include <bsd.own.mk>
+.include <bsd.prog.mk>
Index: src/usr.sbin/kleak/kleak.c
diff -u /dev/null src/usr.sbin/kleak/kleak.c:1.1
--- /dev/null	Sun Dec  2 21:00:14 2018
+++ src/usr.sbin/kleak/kleak.c	Sun Dec  2 21:00:13 2018
@@ -0,0 +1,344 @@
+/*	$NetBSD: kleak.c,v 1.1 2018/12/02 21:00:13 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard. Based on an idea developed by Maxime Villard and
+ * Thomas Barabosch of Fraunhofer FKIE.
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <util.h>
+
+#include <sys/rbtree.h>
+
+#define	_PATH_KSYMS	"/dev/ksyms"
+
+struct sym {
+	char *name;
+	uint64_t value;
+	uint64_t size;
+};
+
+static struct sym **syms = NULL;
+static size_t nsyms = 0;
+
+static int
+compare_value(const void *p1, const void *p2)
+{
+	const struct sym *s1 = *(const struct sym * const *)p1;
+	const struct sym *s2 = *(const struct sym * const *)p2;
+
+	if (s1->value > s2->value) {
+		return -1;
+	} else if (s1->value < s2->value) {
+		return 1;
+	}
+	/*
+	 * to produce a stable result, it's better not to return 0
+	 * even for __strong_alias.
+	 */
+	if (s1->size > s2->size) {
+		return -1;
+	} else if (s1->size < s2->size) {
+		return 1;
+	}
+	return strcmp(s1->name, s2->name);
+}
+
+static void
+ksymload(void)
+{
+	Elf *e;
+	Elf_Scn *s;
+	GElf_Shdr sh_store;
+	GElf_Shdr *sh;
+	Elf_Data *d;
+	int fd;
+	size_t size, i;
+
+	fd = open(_PATH_KSYMS, O_RDONLY);
+	if (fd == -1) {
+		err(EXIT_FAILURE, "open");
+	}
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		goto elffail;
+	}
+	e = elf_begin(fd, ELF_C_READ, NULL);
+	if (e == NULL) {
+		goto elffail;
+	}
+	for (s = elf_nextscn(e, NULL); s != NULL; s = elf_nextscn(e, s)) {
+		sh = gelf_getshdr(s, &sh_store);
+		if (sh == NULL) {
+			goto elffail;
+		}
+		if (sh->sh_type == SHT_SYMTAB) {
+			break;
+		}
+	}
+	if (s == NULL) {
+		errx(EXIT_FAILURE, "no symtab");
+	}
+	d = elf_getdata(s, NULL);
+	if (d == NULL) {
+		goto elffail;
+	}
+	assert(sh->sh_size == d->d_size);
+	size = sh->sh_size / sh->sh_entsize;
+	for (i = 1; i < size; i++) {
+		GElf_Sym st_store;
+		GElf_Sym *st;
+		struct sym *sym;
+
+		st = gelf_getsym(d, (int)i, &st_store);
+		if (st == NULL) {
+			goto elffail;
+		}
+		if (ELF_ST_TYPE(st->st_info) != STT_FUNC) {
+			continue;
+		}
+		sym = emalloc(sizeof(*sym));
+		sym->name = estrdup(elf_strptr(e, sh->sh_link, st->st_name));
+		sym->value = (uint64_t)st->st_value;
+		sym->size = st->st_size;
+		nsyms++;
+		syms = erealloc(syms, sizeof(*syms) * nsyms);
+		syms[nsyms - 1] = sym;
+	}
+	qsort(syms, nsyms, sizeof(*syms), compare_value);
+	return;
+elffail:
+	errx(EXIT_FAILURE, "libelf: %s", elf_errmsg(elf_errno()));
+}
+
+static const char *
+ksymlookup(uint64_t value, uint64_t *offset)
+{
+	size_t hi;
+	size_t lo;
+	size_t i;
+
+	/*
+	 * try to find the smallest i for which syms[i]->value <= value.
+	 * syms[] is ordered by syms[]->value in the descending order.
+	 */
+
+	hi = nsyms - 1;
+	lo = 0;
+	while (lo < hi) {
+		const size_t mid = (lo + hi) / 2;
+		const struct sym *sym = syms[mid];
+
+		assert(syms[lo]->value >= sym->value);
+		assert(sym->value >= syms[hi]->value);
+		if (sym->value <= value) {
+			hi = mid;
+			continue;
+		}
+		lo = mid + 1;
+	}
+	assert(lo == nsyms - 1 || syms[lo]->value <= value);
+	assert(lo == 0 || syms[lo - 1]->value > value);
+	for (i = lo; i < nsyms; i++) {
+		const struct sym *sym = syms[i];
+
+		if (sym->value <= value &&
+		    (sym->size == 0 || value - sym->value <= sym->size )) {
+			*offset = value - sym->value;
+			return sym->name;
+		}
+		if (sym->size != 0 && sym->value + sym->size < value) {
+			break;
+		}
+	}
+	return NULL;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define KLEAK_HIT_MAXPC		16
+#define KLEAK_RESULT_MAXHIT	32
+
+struct kleak_hit {
+	size_t len;
+	size_t leaked;
+	size_t npc;
+	uintptr_t pc[KLEAK_HIT_MAXPC];
+};
+
+struct kleak_result {
+	size_t nhits;
+	size_t nmiss;
+	struct kleak_hit hits[KLEAK_RESULT_MAXHIT];
+};
+
+static int nrounds = 4;
+
+static void kleak_dump(struct kleak_result *result)
+{
+	struct kleak_hit *hit;
+	const char *name;
+	uint64_t offset;
+	size_t i, j;
+
+	for (i = 0; i < result->nhits; i++) {
+		hit = &result->hits[i];
+		printf("+ Possible info leak: "
+		    "[len=%zu, leaked=%zu]\n", hit->len, hit->leaked);
+		for (j = 0; j < hit->npc; j++) {
+			name = ksymlookup(hit->pc[j], &offset);
+			if (name == NULL) {
+				name = "(unknown)";
+			}
+			printf("| #%zu %p in %s\n", j, (void *)hit->pc[j],
+			    name);
+		}
+	}
+
+	printf("> Summary: %d rounds, %zu hits, %zu misses\n",
+	    nrounds, result->nhits, result->nmiss);
+}
+
+__dead static void
+usage(void)
+{
+	fprintf(stderr, "%s [-n nrounds] cmd\n", getprogname());
+	exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+	struct kleak_result result;
+	uint8_t markers[8];
+	size_t len;
+	int i, ch;
+	pid_t pid;
+	bool val;
+
+	while ((ch = getopt(argc, argv, "n:")) != -1) {
+		switch (ch) {
+		case 'n':
+			nrounds = atoi(optarg);
+			break;
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc == 0) {
+		usage();
+	}
+
+	if (nrounds < 1 || nrounds > 8) {
+		usage();
+	}
+
+	ksymload();
+
+	/* In case a previous run was killed. */
+	val = true;
+	sysctlbyname("kleak.stop", NULL, NULL, &val, sizeof(val));
+
+	if (sysctlbyname("kleak.rounds", NULL, NULL, &nrounds, sizeof(nrounds)) == -1)
+		err(EXIT_FAILURE, "couldn't set rounds");
+
+	len = sizeof(markers);
+	if (sysctlbyname("kleak.patterns", &markers, &len, NULL, 0) == -1)
+		err(EXIT_FAILURE, "couldn't set rounds");
+
+	printf("> Launching with markers: 0x%02x 0x%02x 0x%02x 0x%02x "
+	    "0x%02x 0x%02x 0x%02x 0x%02x\n", markers[0], markers[1], markers[2],
+	    markers[3], markers[4], markers[5], markers[6], markers[7]);
+
+	val = true;
+	if (sysctlbyname("kleak.start", NULL, NULL, &val, sizeof(val)) == -1)
+		err(EXIT_FAILURE, "couldn't start kleak");
+
+	for (i = 0; i < nrounds; i++) {
+		printf("[kleak] Pass %d "
+		    "-----------------------------------------------------------\n", i+1);
+
+		pid = fork();
+		switch (pid) {
+		case -1:
+			err(EXIT_FAILURE, "fork");
+		case 0:
+			execvp(argv[0], argv);
+			_Exit(EXIT_FAILURE);
+		}
+		for (;;) {
+			int status;
+
+			pid = wait4(-1, &status, 0, NULL);
+			if (pid == -1) {
+				if (errno == ECHILD) {
+					break;
+				}
+				err(EXIT_FAILURE, "wait4");
+			}
+			if (pid != 0 && WIFEXITED(status)) {
+				break;
+			}
+		}
+
+		if (i < nrounds - 1) {
+			val = true;
+			if (sysctlbyname("kleak.rotate", NULL, NULL, &val, sizeof(val)) == -1)
+				err(EXIT_FAILURE, "couldn't rotate");
+		}
+	}
+
+	val = true;
+	if (sysctlbyname("kleak.stop", NULL, NULL, &val, sizeof(val)) == -1)
+		err(EXIT_FAILURE, "couldn't stop kleak");
+
+	len = sizeof(result);
+	if (sysctlbyname("kleak.result", &result, &len, NULL, 0) == -1)
+		err(EXIT_FAILURE, "couldn't get the result");
+
+	kleak_dump(&result);
+
+	return 0;
+}

Reply via email to