Add the KASAN runtime

KASAN enables the use of LLVM's AddressSanitizer in the kernel.  This
feature makes use of compiler instrumentation to validate memory
accesses in the kernel and detect several types of bugs, including
use-after-frees and out-of-bounds accesses.  It is particularly
effective when combined with test suites or syzkaller.  KASAN has high
CPU and memory usage overhead and so is not suited for production
environments.

The runtime and pmap maintain a shadow of the kernel map to store
information about the validity of memory mapped at a given kernel
address.

The runtime implements a number of functions defined by the compiler
ABI.  These are prefixed by __asan.  The compiler emits calls to
__asan_load*() and __asan_store*() around memory accesses, and the
runtime consults the shadow map to determine whether a given access is
valid.

kasan_mark() is called by various kernel allocators to update state in
the shadow map.  Updates to those allocators will come in subsequent
commits.

The runtime also defines various interceptors.  Some low-level routines
are implemented in assembly and are thus not amenable to compiler
instrumentation.  To handle this, the runtime implements these routines
on behalf of the rest of the kernel.  The sanitizer implementation
validates memory accesses manually before handing off to the real
implementation.

The sanitizer in a KASAN-configured kernel can be disabled by setting
the loader tunable debug.kasan.disable=1.

Obtained from:	NetBSD
Sponsored by:	The FreeBSD Foundation

(cherry picked from commit 38da497a4d)
This commit is contained in:
Mark Johnston 2021-04-13 17:39:19 -04:00
parent 8b3c771fc6
commit 48d2c7cc30
5 changed files with 1335 additions and 0 deletions

View File

@ -186,6 +186,7 @@ MAN= accept_filter.9 \
insmntque.9 \
intro.9 \
ithread.9 \
kasan.9 \
KASSERT.9 \
kern_reboot.9 \
kern_testfrwk.9 \
@ -1314,6 +1315,8 @@ MLINKS+=kernel_mount.9 free_mntarg.9 \
kernel_mount.9 mount_argb.9 \
kernel_mount.9 mount_argf.9 \
kernel_mount.9 mount_argsu.9
MLINKS+=kasan.9 KASAN.9 \
kasan.9 kasan_mark.9
MLINKS+=khelp.9 khelp_add_hhook.9 \
khelp.9 KHELP_DECLARE_MOD.9 \
khelp.9 KHELP_DECLARE_MOD_UMA.9 \

171
share/man/man9/kasan.9 Normal file
View File

@ -0,0 +1,171 @@
.\"-
.\" Copyright (c) 2021 The FreeBSD Foundation
.\"
.\" This documentation was written by Mark Johnston under sponsorship from
.\" the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd April 13, 2021
.Dt KASAN 9
.Os
.Sh NAME
.Nm kasan
.Nd kernel address sanitizer
.Sh SYNOPSIS
To compile KASAN into the kernel, place the following line in your kernel
configuration file:
.Bd -ragged -offset indent
.Cd "options KASAN"
.Ed
.Pp
.Ft void
.Fn kasan_mark "const void *addr" "size_t size" "size_t redzsize" "uint8_t code"
.Sh DESCRIPTION
.Nm
is a subsystem which leverages compiler instrumentation to detect invalid
memory accesses in the kernel.
Currently it is implemented only on the amd64 platform.
.Pp
When
.Nm
is compiled into the kernel, the compiler is configured to emit function
calls upon every memory access.
The functions are implemented by
.Nm
and permit run-time detection of several types of bugs including
use-after-frees, double frees and frees of invalid pointers, and out-of-bounds
accesses.
These protections apply to memory allocated by
.Xr uma 9 ,
.Xr malloc 9
and related functions, and
.Fn kmem_malloc
and related functions,
as well as global variables and kernel stacks.
.Nm
is conservative and will not detect all instances of these types of bugs.
Memory accesses through the kernel map are sanitized, but accesses via the
direct map are not.
When
.Nm
is configured, the kernel aims to minimize its use of the direct map.
.Sh IMPLEMENTATION NOTES
.Nm
is implemented using compiler instrumentation and a kernel runtime.
When a
kernel is built with the KASAN option enabled, the compiler inserts function calls
before most memory accesses in the generated code.
The runtime implements the corresponding functions, which decide whether a
given access is valid.
If not, the runtime prints a warning or panics the kernel, depending on the
value of the
.Sy debug.kasan.panic_on_violation
sysctl/tunable.
.Pp
The
.Nm
runtime works by maintaining a shadow map for the kernel map.
There exists a linear mapping between addresses in the kernel map and addresses
in the shadow map.
The shadow map is used to store information about the current state of
allocations from the kernel map.
For example, when a buffer is returned by
.Xr malloc 9 ,
the corresponding region of the shadow map is marked to indicate that the
buffer is valid.
When it is freed, the shadow map is updated to mark the buffer as invalid.
Accesses to the buffer are intercepted by the
.Nm
runtime and validated using the contents of the shadow map.
.Pp
Upon booting, all kernel memory is marked as valid.
Kernel allocators must mark cached but free buffers as invalid, and must mark
them valid before freeing the kernel virtual address range.
This slightly reduces the effectiveness of
.Nm
but simplifies its maintenance and integration into the kernel.
.Pp
Updates to the shadow map are performed by calling
.Fn kasan_mark .
Parameter
.Fa addr
is the address of the buffer whose shadow is to be updated,
.Fa size
is the usable size of the buffer, and
.Fa redzsize
is the full size of the buffer allocated from lower layers of the system.
.Fa redzsize
must be greater than or equal to
.Fa size .
In some cases kernel allocators will return a buffer larger than that requested
by the consumer; the unused space at the end is referred to as a red zone and is
always marked as invalid.
.Fa code
allows the caller to specify an identifier used when marking a buffer as invalid.
The identifier is included in any reports generated by
.Nm
and helps identify the source of the invalid access.
For instance, when an item is freed to a
.Xr uma 9
zone, the item is marked with
.Dv KASAN_UMA_FREED .
See
.In sys/asan.h
for the available identifiers.
If the entire buffer is to be marked valid, i.e.,
.Fa size
and
.Fa redzsize
are equal,
.Fa code
should be 0.
.Sh SEE ALSO
.Xr malloc 9 ,
.Xr memguard 9 ,
.Xr redzone 9 ,
.Xr uma 9
.Sh HISTORY
.Nm
first appeared in
.Fx 14.0 .
.Sh BUGS
Accesses to kernel memory outside of the kernel map are ignored by the
.Nm
runtime.
When
.Nm
is configured, the kernel memory allocators are configured to use the kernel
map, but some uses of the direct map remain.
For example, on amd64, accesses to page table pages are not tracked.
.Pp
Some kernel memory allocators explicitly permit accesses after an object has
been freed.
These cannot be sanitized by
.Nm .
For example, memory from all
.Xr uma 9
zones initialized with the
.Dv UMA_ZONE_NOFREE
flag are not sanitized.

View File

@ -3900,6 +3900,8 @@ kern/stack_protector.c standard \
compile-with "${NORMAL_C:N-fstack-protector*}"
kern/subr_acl_nfs4.c optional ufs_acl | zfs
kern/subr_acl_posix1e.c optional ufs_acl
kern/subr_asan.c optional kasan \
compile-with "${NORMAL_C:N-fsanitize*}"
kern/subr_autoconf.c standard
kern/subr_blist.c standard
kern/subr_boot.c standard

1091
sys/kern/subr_asan.c Normal file

File diff suppressed because it is too large Load Diff

68
sys/sys/asan.h Normal file
View File

@ -0,0 +1,68 @@
/* $NetBSD: asan.h,v 1.15 2020/09/10 14:10:46 maxv Exp $ */
/*
* Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net
* All rights reserved.
*
* This code is part of the KASAN subsystem of the NetBSD kernel.
*
* 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _SYS_ASAN_H_
#define _SYS_ASAN_H_
#ifdef KASAN
#include <sys/types.h>
/* ASAN constants. Part of the compiler ABI. */
#define KASAN_SHADOW_SCALE 8
#define KASAN_SHADOW_SCALE_SHIFT 3
/* Stack redzone values. Part of the compiler ABI. */
#define KASAN_STACK_LEFT 0xF1
#define KASAN_STACK_MID 0xF2
#define KASAN_STACK_RIGHT 0xF3
#define KASAN_USE_AFTER_RET 0xF5
#define KASAN_USE_AFTER_SCOPE 0xF8
/* Our redzone values. */
#define KASAN_GENERIC_REDZONE 0xFA
#define KASAN_MALLOC_REDZONE 0xFB
#define KASAN_KMEM_REDZONE 0xFC
#define KASAN_UMA_FREED 0xFD
#define KASAN_KSTACK_FREED 0xFE
void kasan_init(void);
void kasan_shadow_map(void *, size_t);
void kasan_mark(const void *, size_t, size_t, uint8_t);
#else /* KASAN */
#define kasan_early_init(u)
#define kasan_init()
#define kasan_shadow_map(a, s)
#define kasan_mark(p, s, l, c)
#endif /* !KASAN */
#endif /* !_SYS_ASAN_H_ */