Skip to content
Permalink
Browse files

Port the NetBSD KCSAN runtime to FreeBSD.

Update the NetBSD Kernel Concurrency Sanitizer (KCSAN) runtime to work in
the FreeBSD kernel. It is a useful tool for finding data races between
threads executing on different CPUs.

This can be enabled by enabling KCSAN in the kernel config, or by using the
GENERIC-KCSAN amd64 kernel. It works on amd64 and arm64, however the later
needs a compiler change to allow -fsanitize=thread that KCSAN uses.

Sponsored by:	DARPA, AFRL
Differential Revision:	https://reviews.freebsd.org/D22315
  • Loading branch information
zxombie committed Nov 21, 2019
1 parent 8f9d694 commit 6e5970c8f44eec5f975888cbfafd1ee5fb5bf4b3
@@ -146,6 +146,10 @@ DEFINE_IFUNC(, int, casueword, (volatile u_long *, u_long, u_long *, u_long))
casueword_smap : casueword_nosmap);
}

#undef copyinstr
#undef copyin
#undef copyout

int copyinstr_nosmap(const void *udaddr, void *kaddr, size_t len,
size_t *lencopied);
int copyinstr_smap(const void *udaddr, void *kaddr, size_t len,
@@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include <sys/callout.h>
#include <sys/cons.h>
#include <sys/cpu.h>
#include <sys/csan.h>
#include <sys/efi.h>
#include <sys/eventhandler.h>
#include <sys/exec.h>
@@ -1899,6 +1900,8 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)

cpu_probe_amdc1e();

kcsan_cpu_init(0);

#ifdef FDT
x86_init_fdt();
#endif
@@ -2722,17 +2725,47 @@ outb_(u_short port, u_char data)

void *memset_std(void *buf, int c, size_t len);
void *memset_erms(void *buf, int c, size_t len);
void *memmove_std(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
void *memmove_erms(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
void *memcpy_std(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
void *memcpy_erms(void * _Nonnull dst, const void * _Nonnull src,
size_t len);

#ifdef KCSAN
/*
* These fail to build as ifuncs when used with KCSAN.
*/
void *
memset(void *buf, int c, size_t len)
{

return memset_std(buf, c, len);
}

void *
memmove(void * _Nonnull dst, const void * _Nonnull src, size_t len)
{

return memmove_std(dst, src, len);
}

void *
memcpy(void * _Nonnull dst, const void * _Nonnull src, size_t len)
{

return memcpy_std(dst, src, len);
}
#else
DEFINE_IFUNC(, void *, memset, (void *, int, size_t))
{

return ((cpu_stdext_feature & CPUID_STDEXT_ERMS) != 0 ?
memset_erms : memset_std);
}

void *memmove_std(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
void *memmove_erms(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
DEFINE_IFUNC(, void *, memmove, (void * _Nonnull, const void * _Nonnull,
size_t))
{
@@ -2741,16 +2774,13 @@ DEFINE_IFUNC(, void *, memmove, (void * _Nonnull, const void * _Nonnull,
memmove_erms : memmove_std);
}

void *memcpy_std(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
void *memcpy_erms(void * _Nonnull dst, const void * _Nonnull src,
size_t len);
DEFINE_IFUNC(, void *, memcpy, (void * _Nonnull, const void * _Nonnull,size_t))
{

return ((cpu_stdext_feature & CPUID_STDEXT_ERMS) != 0 ?
memcpy_erms : memcpy_std);
}
#endif

void pagezero_std(void *addr);
void pagezero_erms(void *addr);
@@ -106,6 +106,7 @@ options VERBOSE_SYSINIT=0 # Support debug.verbose_sysinit, off by default
#options KCOV # Kernel Coverage Sanitizer
# Warning: KUBSAN can result in a kernel too large for loader to load
#options KUBSAN # Kernel Undefined Behavior Sanitizer
#options KCSAN # Kernel Concurrency Sanitizer

# Kernel dump features.
options EKCD # Support for encrypted kernel dumps
@@ -0,0 +1,33 @@
#
# GENERIC-KCSAN -- Kernel Concurrency Sanitizer kernel configuration file
# for FreeBSD/amd64
#
# This configuration file removes several debugging options, including
# WITNESS and INVARIANTS checking, which are known to have significant
# performance impact on running systems. When benchmarking new features
# this kernel should be used instead of the standard GENERIC.
# This kernel configuration should never appear outside of the HEAD
# of the FreeBSD tree.
#
# For more information on this file, please read the config(5) manual page,
# and/or the handbook section on Kernel Configuration Files:
#
# https://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
#
# The handbook is also available locally in /usr/share/doc/handbook
# if you've installed the doc distribution, otherwise always see the
# FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the
# latest information.
#
# An exhaustive list of options and more detailed explanations of the
# device lines is also present in the ../../conf/NOTES and NOTES files.
# If you are in doubt as to the purpose or necessity of a line, check first
# in NOTES.
#
# $FreeBSD$

include GENERIC

ident GENERIC-KCSAN

options KCSAN
@@ -57,6 +57,20 @@
#define wmb() __asm __volatile("sfence;" : : : "memory")
#define rmb() __asm __volatile("lfence;" : : : "memory")

#ifdef _KERNEL
/*
* OFFSETOF_MONITORBUF == __pcpu_offset(pc_monitorbuf).
*
* The open-coded number is used instead of the symbolic expression to
* avoid a dependency on sys/pcpu.h in machine/atomic.h consumers.
* An assertion in amd64/vm_machdep.c ensures that the value is correct.
*/
#define OFFSETOF_MONITORBUF 0x100
#endif

#if defined(KCSAN) && !defined(KCSAN_RUNTIME)
#include <sys/_cscan_atomic.h>
#else
#include <sys/atomic_common.h>

/*
@@ -345,15 +359,6 @@ atomic_testandclear_long(volatile u_long *p, u_int v)

#if defined(_KERNEL)

/*
* OFFSETOF_MONITORBUF == __pcpu_offset(pc_monitorbuf).
*
* The open-coded number is used instead of the symbolic expression to
* avoid a dependency on sys/pcpu.h in machine/atomic.h consumers.
* An assertion in amd64/vm_machdep.c ensures that the value is correct.
*/
#define OFFSETOF_MONITORBUF 0x100

#if defined(SMP) || defined(KLD_MODULE)
static __inline void
__storeload_barrier(void)
@@ -679,4 +684,6 @@ u_long atomic_swap_long(volatile u_long *p, u_long v);

#endif /* !WANT_FUNCTIONS */

#endif /* KCSAN && !KCSAN_RUNTIME */

#endif /* !_MACHINE_ATOMIC_H_ */
@@ -0,0 +1,67 @@
/* $NetBSD: csan.h,v 1.2 2019/11/06 06:57:22 maxv Exp $ */

/*
* Copyright (c) 2019 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.
*
* $FreeBSD$
*/

#include <machine/cpufunc.h>
#include <machine/stack.h>
#include <machine/vmparam.h>

static inline bool
kcsan_md_is_avail(void)
{
return true;
}

static inline void
kcsan_md_disable_intrs(uint64_t *state)
{

*state = intr_disable();
}

static inline void
kcsan_md_enable_intrs(uint64_t *state)
{

intr_restore(*state);
}

static inline void
kcsan_md_delay(uint64_t us)
{
DELAY(us);
}

static void
kcsan_md_unwind(void)
{
}
@@ -25,6 +25,8 @@
*
*/

#define KCSAN_RUNTIME

#include "opt_platform.h"

#include <sys/param.h>
@@ -32,7 +32,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>

int
copystr(const void * __restrict kfaddr, void * __restrict kdaddr, size_t len,
(copystr)(const void * __restrict kfaddr, void * __restrict kdaddr, size_t len,
size_t * __restrict lencopied)
{
const char *src;
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/cons.h>
#include <sys/cpu.h>
#include <sys/csan.h>
#include <sys/devmap.h>
#include <sys/efi.h>
#include <sys/exec.h>
@@ -1209,6 +1210,8 @@ initarm(struct arm64_bootparams *abp)
kdb_init();
pan_enable();

kcsan_cpu_init(0);

env = kern_getenv("kernelname");
if (env != NULL)
strlcpy(kernelname, env, sizeof(kernelname));
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/csan.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/malloc.h>
@@ -253,6 +254,8 @@ init_secondary(uint64_t cpu)

mtx_unlock_spin(&ap_boot_mtx);

kcsan_cpu_init(cpu);

/* Enter the scheduler */
sched_throw(NULL);

@@ -100,6 +100,7 @@ options VERBOSE_SYSINIT=0 # Support debug.verbose_sysinit, off by default
#options KCOV # Kernel Coverage Sanitizer
# Warning: KUBSAN can result in a kernel too large for loader to load
#options KUBSAN # Kernel Undefined Behavior Sanitizer
#options KCSAN # Kernel Concurrency Sanitizer

# Kernel dump features.
options EKCD # Support for encrypted kernel dumps
@@ -29,8 +29,6 @@
#ifndef _MACHINE_ATOMIC_H_
#define _MACHINE_ATOMIC_H_

#include <sys/atomic_common.h>

#define isb() __asm __volatile("isb" : : : "memory")

/*
@@ -55,6 +53,12 @@
#define wmb() dmb(st) /* Full system memory barrier store */
#define rmb() dmb(ld) /* Full system memory barrier load */

#if defined(KCSAN) && !defined(KCSAN_RUNTIME)
#include <sys/_cscan_atomic.h>
#else

#include <sys/atomic_common.h>

#define ATOMIC_OP(op, asm_op, bar, a, l) \
static __inline void \
atomic_##op##_##bar##8(volatile uint8_t *p, uint8_t val) \
@@ -601,5 +605,7 @@ atomic_thread_fence_seq_cst(void)
dmb(sy);
}

#endif /* KCSAN && !KCSAN_RUNTIME */

#endif /* _MACHINE_ATOMIC_H_ */

@@ -89,6 +89,9 @@
#define BUS_SPACE_BARRIER_READ 0x01
#define BUS_SPACE_BARRIER_WRITE 0x02

#if defined(KCSAN) && !defined(KCSAN_RUNTIME)
#include <sys/_cscan_bus.h>
#else

struct bus_space {
/* cookie */
@@ -464,6 +467,8 @@ struct bus_space {
#define bus_space_copy_region_8(t, h1, o1, h2, o2, c) \
__bs_copy(8, t, h1, o1, h2, o2, c)

#endif

#include <machine/bus_dma.h>

#endif /* _MACHINE_BUS_H_ */

0 comments on commit 6e5970c

Please sign in to comment.
You can’t perform that action at this time.