freebsd-dev/sys/kern/subr_asan.c

1094 lines
30 KiB
C
Raw Normal View History

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 MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29416
2021-04-13 21:39:19 +00:00
/* $NetBSD: subr_asan.c,v 1.26 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.
*/
#define SAN_RUNTIME
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#if 0
__KERNEL_RCSID(0, "$NetBSD: subr_asan.c,v 1.26 2020/09/10 14:10:46 maxv Exp $");
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/asan.h>
#include <sys/kernel.h>
#include <sys/stack.h>
#include <sys/sysctl.h>
#include <machine/asan.h>
/* ASAN constants. Part of the compiler ABI. */
#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE - 1)
#define KASAN_ALLOCA_SCALE_SIZE 32
/* ASAN ABI version. */
#if defined(__clang__) && (__clang_major__ - 0 >= 6)
#define ASAN_ABI_VERSION 8
#elif __GNUC_PREREQ__(7, 1) && !defined(__clang__)
#define ASAN_ABI_VERSION 8
#elif __GNUC_PREREQ__(6, 1) && !defined(__clang__)
#define ASAN_ABI_VERSION 6
#else
#error "Unsupported compiler version"
#endif
#define __RET_ADDR (unsigned long)__builtin_return_address(0)
/* Global variable descriptor. Part of the compiler ABI. */
struct __asan_global_source_location {
const char *filename;
int line_no;
int column_no;
};
struct __asan_global {
const void *beg; /* address of the global variable */
size_t size; /* size of the global variable */
size_t size_with_redzone; /* size with the redzone */
const void *name; /* name of the variable */
const void *module_name; /* name of the module where the var is declared */
unsigned long has_dynamic_init; /* the var has dyn initializer (c++) */
struct __asan_global_source_location *location;
#if ASAN_ABI_VERSION >= 7
uintptr_t odr_indicator; /* the address of the ODR indicator symbol */
#endif
};
FEATURE(kasan, "Kernel address sanitizer");
static SYSCTL_NODE(_debug, OID_AUTO, kasan, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"KASAN options");
static int panic_on_violation = 1;
SYSCTL_INT(_debug_kasan, OID_AUTO, panic_on_violation, CTLFLAG_RDTUN,
&panic_on_violation, 0,
"Panic if an invalid access is detected");
static bool kasan_enabled __read_mostly = false;
/* -------------------------------------------------------------------------- */
void
kasan_shadow_map(vm_offset_t addr, size_t size)
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 MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29416
2021-04-13 21:39:19 +00:00
{
size_t sz, npages, i;
vm_offset_t sva, eva;
KASSERT(addr % KASAN_SHADOW_SCALE == 0,
("%s: invalid address %#lx", __func__, addr));
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 MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29416
2021-04-13 21:39:19 +00:00
sz = roundup(size, KASAN_SHADOW_SCALE) / KASAN_SHADOW_SCALE;
sva = kasan_md_addr_to_shad(addr);
eva = kasan_md_addr_to_shad(addr) + sz;
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 MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29416
2021-04-13 21:39:19 +00:00
sva = rounddown(sva, PAGE_SIZE);
eva = roundup(eva, PAGE_SIZE);
npages = (eva - sva) / PAGE_SIZE;
KASSERT(sva >= KASAN_MIN_ADDRESS && eva < KASAN_MAX_ADDRESS,
("%s: invalid address range %#lx-%#lx", __func__, sva, eva));
for (i = 0; i < npages; i++)
pmap_kasan_enter(sva + ptoa(i));
}
void
kasan_init(void)
{
int disabled;
disabled = 0;
TUNABLE_INT_FETCH("debug.kasan.disabled", &disabled);
if (disabled)
return;
/* MD initialization. */
kasan_md_init();
/* Now officially enabled. */
kasan_enabled = true;
}
static inline const char *
kasan_code_name(uint8_t code)
{
switch (code) {
case KASAN_GENERIC_REDZONE:
return "GenericRedZone";
case KASAN_MALLOC_REDZONE:
return "MallocRedZone";
case KASAN_KMEM_REDZONE:
return "KmemRedZone";
case KASAN_UMA_FREED:
return "UMAUseAfterFree";
case KASAN_KSTACK_FREED:
return "KernelStack";
case KASAN_EXEC_ARGS_FREED:
return "ExecKVA";
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 MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29416
2021-04-13 21:39:19 +00:00
case 1 ... 7:
return "RedZonePartial";
case KASAN_STACK_LEFT:
return "StackLeft";
case KASAN_STACK_MID:
return "StackMiddle";
case KASAN_STACK_RIGHT:
return "StackRight";
case KASAN_USE_AFTER_RET:
return "UseAfterRet";
case KASAN_USE_AFTER_SCOPE:
return "UseAfterScope";
default:
return "Unknown";
}
}
#define REPORT(f, ...) do { \
if (panic_on_violation) { \
panic(f, __VA_ARGS__); \
} else { \
struct stack st; \
\
stack_save(&st); \
printf(f "\n", __VA_ARGS__); \
stack_print_ddb(&st); \
} \
} while (0)
static void
kasan_report(unsigned long addr, size_t size, bool write, unsigned long pc,
uint8_t code)
{
REPORT("ASan: Invalid access, %zu-byte %s at %#lx, %s(%x)",
size, (write ? "write" : "read"), addr, kasan_code_name(code),
code);
}
static __always_inline void
kasan_shadow_1byte_markvalid(unsigned long addr)
{
int8_t *byte = (int8_t *)kasan_md_addr_to_shad(addr);
int8_t last = (addr & KASAN_SHADOW_MASK) + 1;
*byte = last;
}
static __always_inline void
kasan_shadow_Nbyte_markvalid(const void *addr, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
kasan_shadow_1byte_markvalid((unsigned long)addr + i);
}
}
static __always_inline void
kasan_shadow_Nbyte_fill(const void *addr, size_t size, uint8_t code)
{
void *shad;
if (__predict_false(size == 0))
return;
if (__predict_false(kasan_md_unsupported((vm_offset_t)addr)))
return;
KASSERT((vm_offset_t)addr % KASAN_SHADOW_SCALE == 0,
("%s: invalid address %p", __func__, addr));
KASSERT(size % KASAN_SHADOW_SCALE == 0,
("%s: invalid size %zu", __func__, size));
shad = (void *)kasan_md_addr_to_shad((uintptr_t)addr);
size = size >> KASAN_SHADOW_SCALE_SHIFT;
__builtin_memset(shad, code, size);
}
/*
* In an area of size 'sz_with_redz', mark the 'size' first bytes as valid,
* and the rest as invalid. There are generally two use cases:
*
* o kasan_mark(addr, origsize, size, code), with origsize < size. This marks
* the redzone at the end of the buffer as invalid. If the entire is to be
* marked invalid, origsize will be 0.
*
* o kasan_mark(addr, size, size, 0). This marks the entire buffer as valid.
*/
void
kasan_mark(const void *addr, size_t size, size_t redzsize, uint8_t code)
{
size_t i, n, redz;
int8_t *shad;
if ((vm_offset_t)addr >= DMAP_MIN_ADDRESS &&
(vm_offset_t)addr < DMAP_MAX_ADDRESS)
return;
KASSERT((vm_offset_t)addr >= VM_MIN_KERNEL_ADDRESS &&
(vm_offset_t)addr < VM_MAX_KERNEL_ADDRESS,
("%s: invalid address %p", __func__, addr));
KASSERT((vm_offset_t)addr % KASAN_SHADOW_SCALE == 0,
("%s: invalid address %p", __func__, addr));
redz = redzsize - roundup(size, KASAN_SHADOW_SCALE);
KASSERT(redz % KASAN_SHADOW_SCALE == 0,
("%s: invalid size %zu", __func__, redz));
shad = (int8_t *)kasan_md_addr_to_shad((uintptr_t)addr);
/* Chunks of 8 bytes, valid. */
n = size / KASAN_SHADOW_SCALE;
for (i = 0; i < n; i++) {
*shad++ = 0;
}
/* Possibly one chunk, mid. */
if ((size & KASAN_SHADOW_MASK) != 0) {
*shad++ = (size & KASAN_SHADOW_MASK);
}
/* Chunks of 8 bytes, invalid. */
n = redz / KASAN_SHADOW_SCALE;
for (i = 0; i < n; i++) {
*shad++ = code;
}
}
/* -------------------------------------------------------------------------- */
#define ADDR_CROSSES_SCALE_BOUNDARY(addr, size) \
(addr >> KASAN_SHADOW_SCALE_SHIFT) != \
((addr + size - 1) >> KASAN_SHADOW_SCALE_SHIFT)
static __always_inline bool
kasan_shadow_1byte_isvalid(unsigned long addr, uint8_t *code)
{
int8_t *byte = (int8_t *)kasan_md_addr_to_shad(addr);
int8_t last = (addr & KASAN_SHADOW_MASK) + 1;
if (__predict_true(*byte == 0 || last <= *byte)) {
return (true);
}
*code = *byte;
return (false);
}
static __always_inline bool
kasan_shadow_2byte_isvalid(unsigned long addr, uint8_t *code)
{
int8_t *byte, last;
if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 2)) {
return (kasan_shadow_1byte_isvalid(addr, code) &&
kasan_shadow_1byte_isvalid(addr+1, code));
}
byte = (int8_t *)kasan_md_addr_to_shad(addr);
last = ((addr + 1) & KASAN_SHADOW_MASK) + 1;
if (__predict_true(*byte == 0 || last <= *byte)) {
return (true);
}
*code = *byte;
return (false);
}
static __always_inline bool
kasan_shadow_4byte_isvalid(unsigned long addr, uint8_t *code)
{
int8_t *byte, last;
if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 4)) {
return (kasan_shadow_2byte_isvalid(addr, code) &&
kasan_shadow_2byte_isvalid(addr+2, code));
}
byte = (int8_t *)kasan_md_addr_to_shad(addr);
last = ((addr + 3) & KASAN_SHADOW_MASK) + 1;
if (__predict_true(*byte == 0 || last <= *byte)) {
return (true);
}
*code = *byte;
return (false);
}
static __always_inline bool
kasan_shadow_8byte_isvalid(unsigned long addr, uint8_t *code)
{
int8_t *byte, last;
if (ADDR_CROSSES_SCALE_BOUNDARY(addr, 8)) {
return (kasan_shadow_4byte_isvalid(addr, code) &&
kasan_shadow_4byte_isvalid(addr+4, code));
}
byte = (int8_t *)kasan_md_addr_to_shad(addr);
last = ((addr + 7) & KASAN_SHADOW_MASK) + 1;
if (__predict_true(*byte == 0 || last <= *byte)) {
return (true);
}
*code = *byte;
return (false);
}
static __always_inline bool
kasan_shadow_Nbyte_isvalid(unsigned long addr, size_t size, uint8_t *code)
{
size_t i;
for (i = 0; i < size; i++) {
if (!kasan_shadow_1byte_isvalid(addr+i, code))
return (false);
}
return (true);
}
static __always_inline void
kasan_shadow_check(unsigned long addr, size_t size, bool write,
unsigned long retaddr)
{
uint8_t code;
bool valid;
if (__predict_false(!kasan_enabled))
return;
if (__predict_false(size == 0))
return;
if (__predict_false(kasan_md_unsupported(addr)))
return;
if (__predict_false(panicstr != NULL))
return;
if (__builtin_constant_p(size)) {
switch (size) {
case 1:
valid = kasan_shadow_1byte_isvalid(addr, &code);
break;
case 2:
valid = kasan_shadow_2byte_isvalid(addr, &code);
break;
case 4:
valid = kasan_shadow_4byte_isvalid(addr, &code);
break;
case 8:
valid = kasan_shadow_8byte_isvalid(addr, &code);
break;
default:
valid = kasan_shadow_Nbyte_isvalid(addr, size, &code);
break;
}
} else {
valid = kasan_shadow_Nbyte_isvalid(addr, size, &code);
}
if (__predict_false(!valid)) {
kasan_report(addr, size, write, retaddr, code);
}
}
/* -------------------------------------------------------------------------- */
void *
kasan_memcpy(void *dst, const void *src, size_t len)
{
kasan_shadow_check((unsigned long)src, len, false, __RET_ADDR);
kasan_shadow_check((unsigned long)dst, len, true, __RET_ADDR);
return (__builtin_memcpy(dst, src, len));
}
int
kasan_memcmp(const void *b1, const void *b2, size_t len)
{
kasan_shadow_check((unsigned long)b1, len, false, __RET_ADDR);
kasan_shadow_check((unsigned long)b2, len, false, __RET_ADDR);
return (__builtin_memcmp(b1, b2, len));
}
void *
kasan_memset(void *b, int c, size_t len)
{
kasan_shadow_check((unsigned long)b, len, true, __RET_ADDR);
return (__builtin_memset(b, c, len));
}
void *
kasan_memmove(void *dst, const void *src, size_t len)
{
kasan_shadow_check((unsigned long)src, len, false, __RET_ADDR);
kasan_shadow_check((unsigned long)dst, len, true, __RET_ADDR);
return (__builtin_memmove(dst, src, len));
}
size_t
kasan_strlen(const char *str)
{
const char *s;
s = str;
while (1) {
kasan_shadow_check((unsigned long)s, 1, false, __RET_ADDR);
if (*s == '\0')
break;
s++;
}
return (s - str);
}
char *
kasan_strcpy(char *dst, const char *src)
{
char *save = dst;
while (1) {
kasan_shadow_check((unsigned long)src, 1, false, __RET_ADDR);
kasan_shadow_check((unsigned long)dst, 1, true, __RET_ADDR);
*dst = *src;
if (*src == '\0')
break;
src++, dst++;
}
return save;
}
int
kasan_strcmp(const char *s1, const char *s2)
{
while (1) {
kasan_shadow_check((unsigned long)s1, 1, false, __RET_ADDR);
kasan_shadow_check((unsigned long)s2, 1, false, __RET_ADDR);
if (*s1 != *s2)
break;
if (*s1 == '\0')
return 0;
s1++, s2++;
}
return (*(const unsigned char *)s1 - *(const unsigned char *)s2);
}
int
kasan_copyin(const void *uaddr, void *kaddr, size_t len)
{
kasan_shadow_check((unsigned long)kaddr, len, true, __RET_ADDR);
return (copyin(uaddr, kaddr, len));
}
int
kasan_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done)
{
kasan_shadow_check((unsigned long)kaddr, len, true, __RET_ADDR);
return (copyinstr(uaddr, kaddr, len, done));
}
int
kasan_copyout(const void *kaddr, void *uaddr, size_t len)
{
kasan_shadow_check((unsigned long)kaddr, len, false, __RET_ADDR);
return (copyout(kaddr, uaddr, len));
}
/* -------------------------------------------------------------------------- */
#include <machine/atomic.h>
#define ATOMIC_SAN_PREFIX kasan
#include <sys/atomic_san.h>
#define _ASAN_ATOMIC_FUNC_ADD(name, type) \
void kasan_atomic_add_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
atomic_add_##name(ptr, val); \
}
#define ASAN_ATOMIC_FUNC_ADD(name, type) \
_ASAN_ATOMIC_FUNC_ADD(name, type) \
_ASAN_ATOMIC_FUNC_ADD(acq_##name, type) \
_ASAN_ATOMIC_FUNC_ADD(rel_##name, type)
#define _ASAN_ATOMIC_FUNC_SUBTRACT(name, type) \
void kasan_atomic_subtract_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
atomic_subtract_##name(ptr, val); \
}
#define ASAN_ATOMIC_FUNC_SUBTRACT(name, type) \
_ASAN_ATOMIC_FUNC_SUBTRACT(name, type) \
_ASAN_ATOMIC_FUNC_SUBTRACT(acq_##name, type) \
_ASAN_ATOMIC_FUNC_SUBTRACT(rel_##name, type)
#define _ASAN_ATOMIC_FUNC_SET(name, type) \
void kasan_atomic_set_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
atomic_set_##name(ptr, val); \
}
#define ASAN_ATOMIC_FUNC_SET(name, type) \
_ASAN_ATOMIC_FUNC_SET(name, type) \
_ASAN_ATOMIC_FUNC_SET(acq_##name, type) \
_ASAN_ATOMIC_FUNC_SET(rel_##name, type)
#define _ASAN_ATOMIC_FUNC_CLEAR(name, type) \
void kasan_atomic_clear_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
atomic_clear_##name(ptr, val); \
}
#define ASAN_ATOMIC_FUNC_CLEAR(name, type) \
_ASAN_ATOMIC_FUNC_CLEAR(name, type) \
_ASAN_ATOMIC_FUNC_CLEAR(acq_##name, type) \
_ASAN_ATOMIC_FUNC_CLEAR(rel_##name, type)
#define ASAN_ATOMIC_FUNC_FETCHADD(name, type) \
type kasan_atomic_fetchadd_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_fetchadd_##name(ptr, val)); \
}
#define ASAN_ATOMIC_FUNC_READANDCLEAR(name, type) \
type kasan_atomic_readandclear_##name(volatile type *ptr) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_readandclear_##name(ptr)); \
}
#define ASAN_ATOMIC_FUNC_TESTANDCLEAR(name, type) \
int kasan_atomic_testandclear_##name(volatile type *ptr, u_int v) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_testandclear_##name(ptr, v)); \
}
#define ASAN_ATOMIC_FUNC_TESTANDSET(name, type) \
int kasan_atomic_testandset_##name(volatile type *ptr, u_int v) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_testandset_##name(ptr, v)); \
}
#define ASAN_ATOMIC_FUNC_SWAP(name, type) \
type kasan_atomic_swap_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_swap_##name(ptr, val)); \
}
#define _ASAN_ATOMIC_FUNC_CMPSET(name, type) \
int kasan_atomic_cmpset_##name(volatile type *ptr, type oval, \
type nval) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_cmpset_##name(ptr, oval, nval)); \
}
#define ASAN_ATOMIC_FUNC_CMPSET(name, type) \
_ASAN_ATOMIC_FUNC_CMPSET(name, type) \
_ASAN_ATOMIC_FUNC_CMPSET(acq_##name, type) \
_ASAN_ATOMIC_FUNC_CMPSET(rel_##name, type)
#define _ASAN_ATOMIC_FUNC_FCMPSET(name, type) \
int kasan_atomic_fcmpset_##name(volatile type *ptr, type *oval, \
type nval) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_fcmpset_##name(ptr, oval, nval)); \
}
#define ASAN_ATOMIC_FUNC_FCMPSET(name, type) \
_ASAN_ATOMIC_FUNC_FCMPSET(name, type) \
_ASAN_ATOMIC_FUNC_FCMPSET(acq_##name, type) \
_ASAN_ATOMIC_FUNC_FCMPSET(rel_##name, type)
#define ASAN_ATOMIC_FUNC_THREAD_FENCE(name) \
void kasan_atomic_thread_fence_##name(void) \
{ \
atomic_thread_fence_##name(); \
}
#define _ASAN_ATOMIC_FUNC_LOAD(name, type) \
type kasan_atomic_load_##name(volatile type *ptr) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
return (atomic_load_##name(ptr)); \
}
#define ASAN_ATOMIC_FUNC_LOAD(name, type) \
_ASAN_ATOMIC_FUNC_LOAD(name, type) \
_ASAN_ATOMIC_FUNC_LOAD(acq_##name, type)
#define _ASAN_ATOMIC_FUNC_STORE(name, type) \
void kasan_atomic_store_##name(volatile type *ptr, type val) \
{ \
kasan_shadow_check((uintptr_t)ptr, sizeof(type), true, \
__RET_ADDR); \
atomic_store_##name(ptr, val); \
}
#define ASAN_ATOMIC_FUNC_STORE(name, type) \
_ASAN_ATOMIC_FUNC_STORE(name, type) \
_ASAN_ATOMIC_FUNC_STORE(rel_##name, type)
ASAN_ATOMIC_FUNC_ADD(8, uint8_t);
ASAN_ATOMIC_FUNC_ADD(16, uint16_t);
ASAN_ATOMIC_FUNC_ADD(32, uint32_t);
ASAN_ATOMIC_FUNC_ADD(64, uint64_t);
ASAN_ATOMIC_FUNC_ADD(int, u_int);
ASAN_ATOMIC_FUNC_ADD(long, u_long);
ASAN_ATOMIC_FUNC_ADD(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_SUBTRACT(8, uint8_t);
ASAN_ATOMIC_FUNC_SUBTRACT(16, uint16_t);
ASAN_ATOMIC_FUNC_SUBTRACT(32, uint32_t);
ASAN_ATOMIC_FUNC_SUBTRACT(64, uint64_t);
ASAN_ATOMIC_FUNC_SUBTRACT(int, u_int);
ASAN_ATOMIC_FUNC_SUBTRACT(long, u_long);
ASAN_ATOMIC_FUNC_SUBTRACT(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_SET(8, uint8_t);
ASAN_ATOMIC_FUNC_SET(16, uint16_t);
ASAN_ATOMIC_FUNC_SET(32, uint32_t);
ASAN_ATOMIC_FUNC_SET(64, uint64_t);
ASAN_ATOMIC_FUNC_SET(int, u_int);
ASAN_ATOMIC_FUNC_SET(long, u_long);
ASAN_ATOMIC_FUNC_SET(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_CLEAR(8, uint8_t);
ASAN_ATOMIC_FUNC_CLEAR(16, uint16_t);
ASAN_ATOMIC_FUNC_CLEAR(32, uint32_t);
ASAN_ATOMIC_FUNC_CLEAR(64, uint64_t);
ASAN_ATOMIC_FUNC_CLEAR(int, u_int);
ASAN_ATOMIC_FUNC_CLEAR(long, u_long);
ASAN_ATOMIC_FUNC_CLEAR(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_FETCHADD(32, uint32_t);
ASAN_ATOMIC_FUNC_FETCHADD(64, uint64_t);
ASAN_ATOMIC_FUNC_FETCHADD(int, u_int);
ASAN_ATOMIC_FUNC_FETCHADD(long, u_long);
ASAN_ATOMIC_FUNC_READANDCLEAR(32, uint32_t);
ASAN_ATOMIC_FUNC_READANDCLEAR(64, uint64_t);
ASAN_ATOMIC_FUNC_READANDCLEAR(int, u_int);
ASAN_ATOMIC_FUNC_READANDCLEAR(long, u_long);
ASAN_ATOMIC_FUNC_READANDCLEAR(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_TESTANDCLEAR(32, uint32_t);
ASAN_ATOMIC_FUNC_TESTANDCLEAR(64, uint64_t);
ASAN_ATOMIC_FUNC_TESTANDCLEAR(int, u_int);
ASAN_ATOMIC_FUNC_TESTANDCLEAR(long, u_long);
ASAN_ATOMIC_FUNC_TESTANDSET(32, uint32_t);
ASAN_ATOMIC_FUNC_TESTANDSET(64, uint64_t);
ASAN_ATOMIC_FUNC_TESTANDSET(int, u_int);
ASAN_ATOMIC_FUNC_TESTANDSET(long, u_long);
ASAN_ATOMIC_FUNC_SWAP(32, uint32_t);
ASAN_ATOMIC_FUNC_SWAP(64, uint64_t);
ASAN_ATOMIC_FUNC_SWAP(int, u_int);
ASAN_ATOMIC_FUNC_SWAP(long, u_long);
ASAN_ATOMIC_FUNC_SWAP(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_CMPSET(8, uint8_t);
ASAN_ATOMIC_FUNC_CMPSET(16, uint16_t);
ASAN_ATOMIC_FUNC_CMPSET(32, uint32_t);
ASAN_ATOMIC_FUNC_CMPSET(64, uint64_t);
ASAN_ATOMIC_FUNC_CMPSET(int, u_int);
ASAN_ATOMIC_FUNC_CMPSET(long, u_long);
ASAN_ATOMIC_FUNC_CMPSET(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_FCMPSET(8, uint8_t);
ASAN_ATOMIC_FUNC_FCMPSET(16, uint16_t);
ASAN_ATOMIC_FUNC_FCMPSET(32, uint32_t);
ASAN_ATOMIC_FUNC_FCMPSET(64, uint64_t);
ASAN_ATOMIC_FUNC_FCMPSET(int, u_int);
ASAN_ATOMIC_FUNC_FCMPSET(long, u_long);
ASAN_ATOMIC_FUNC_FCMPSET(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_LOAD(8, uint8_t);
ASAN_ATOMIC_FUNC_LOAD(16, uint16_t);
ASAN_ATOMIC_FUNC_LOAD(32, uint32_t);
ASAN_ATOMIC_FUNC_LOAD(64, uint64_t);
ASAN_ATOMIC_FUNC_LOAD(char, u_char);
ASAN_ATOMIC_FUNC_LOAD(short, u_short);
ASAN_ATOMIC_FUNC_LOAD(int, u_int);
ASAN_ATOMIC_FUNC_LOAD(long, u_long);
ASAN_ATOMIC_FUNC_LOAD(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_STORE(8, uint8_t);
ASAN_ATOMIC_FUNC_STORE(16, uint16_t);
ASAN_ATOMIC_FUNC_STORE(32, uint32_t);
ASAN_ATOMIC_FUNC_STORE(64, uint64_t);
ASAN_ATOMIC_FUNC_STORE(char, u_char);
ASAN_ATOMIC_FUNC_STORE(short, u_short);
ASAN_ATOMIC_FUNC_STORE(int, u_int);
ASAN_ATOMIC_FUNC_STORE(long, u_long);
ASAN_ATOMIC_FUNC_STORE(ptr, uintptr_t);
ASAN_ATOMIC_FUNC_THREAD_FENCE(acq);
ASAN_ATOMIC_FUNC_THREAD_FENCE(rel);
ASAN_ATOMIC_FUNC_THREAD_FENCE(acq_rel);
ASAN_ATOMIC_FUNC_THREAD_FENCE(seq_cst);
void
kasan_atomic_interrupt_fence(void)
{
}
/* -------------------------------------------------------------------------- */
#include <sys/bus.h>
#include <machine/bus.h>
#define BUS_SAN_PREFIX kasan
#include <sys/bus_san.h>
int
kasan_bus_space_map(bus_space_tag_t tag, bus_addr_t hnd, bus_size_t size,
int flags, bus_space_handle_t *handlep)
{
return (bus_space_map(tag, hnd, size, flags, handlep));
}
void
kasan_bus_space_unmap(bus_space_tag_t tag, bus_space_handle_t hnd,
bus_size_t size)
{
bus_space_unmap(tag, hnd, size);
}
int
kasan_bus_space_subregion(bus_space_tag_t tag, bus_space_handle_t hnd,
bus_size_t offset, bus_size_t size, bus_space_handle_t *handlep)
{
return (bus_space_subregion(tag, hnd, offset, size, handlep));
}
void
kasan_bus_space_free(bus_space_tag_t tag, bus_space_handle_t hnd,
bus_size_t size)
{
bus_space_free(tag, hnd, size);
}
void
kasan_bus_space_barrier(bus_space_tag_t tag, bus_space_handle_t hnd,
bus_size_t offset, bus_size_t size, int flags)
{
bus_space_barrier(tag, hnd, offset, size, flags);
}
#define ASAN_BUS_READ_FUNC(func, width, type) \
type kasan_bus_space_read##func##_##width(bus_space_tag_t tag, \
bus_space_handle_t hnd, bus_size_t offset) \
{ \
return (bus_space_read##func##_##width(tag, hnd, \
offset)); \
} \
#define ASAN_BUS_READ_PTR_FUNC(func, width, type) \
void kasan_bus_space_read_##func##_##width(bus_space_tag_t tag, \
bus_space_handle_t hnd, bus_size_t size, type *buf, \
bus_size_t count) \
{ \
kasan_shadow_check((uintptr_t)buf, sizeof(type) * count,\
false, __RET_ADDR); \
bus_space_read_##func##_##width(tag, hnd, size, buf, \
count); \
}
ASAN_BUS_READ_FUNC(, 1, uint8_t)
ASAN_BUS_READ_FUNC(_stream, 1, uint8_t)
ASAN_BUS_READ_PTR_FUNC(multi, 1, uint8_t)
ASAN_BUS_READ_PTR_FUNC(multi_stream, 1, uint8_t)
ASAN_BUS_READ_PTR_FUNC(region, 1, uint8_t)
ASAN_BUS_READ_PTR_FUNC(region_stream, 1, uint8_t)
ASAN_BUS_READ_FUNC(, 2, uint16_t)
ASAN_BUS_READ_FUNC(_stream, 2, uint16_t)
ASAN_BUS_READ_PTR_FUNC(multi, 2, uint16_t)
ASAN_BUS_READ_PTR_FUNC(multi_stream, 2, uint16_t)
ASAN_BUS_READ_PTR_FUNC(region, 2, uint16_t)
ASAN_BUS_READ_PTR_FUNC(region_stream, 2, uint16_t)
ASAN_BUS_READ_FUNC(, 4, uint32_t)
ASAN_BUS_READ_FUNC(_stream, 4, uint32_t)
ASAN_BUS_READ_PTR_FUNC(multi, 4, uint32_t)
ASAN_BUS_READ_PTR_FUNC(multi_stream, 4, uint32_t)
ASAN_BUS_READ_PTR_FUNC(region, 4, uint32_t)
ASAN_BUS_READ_PTR_FUNC(region_stream, 4, uint32_t)
ASAN_BUS_READ_FUNC(, 8, uint64_t)
#define ASAN_BUS_WRITE_FUNC(func, width, type) \
void kasan_bus_space_write##func##_##width(bus_space_tag_t tag, \
bus_space_handle_t hnd, bus_size_t offset, type value) \
{ \
bus_space_write##func##_##width(tag, hnd, offset, value);\
} \
#define ASAN_BUS_WRITE_PTR_FUNC(func, width, type) \
void kasan_bus_space_write_##func##_##width(bus_space_tag_t tag,\
bus_space_handle_t hnd, bus_size_t size, const type *buf, \
bus_size_t count) \
{ \
kasan_shadow_check((uintptr_t)buf, sizeof(type) * count,\
true, __RET_ADDR); \
bus_space_write_##func##_##width(tag, hnd, size, buf, \
count); \
}
ASAN_BUS_WRITE_FUNC(, 1, uint8_t)
ASAN_BUS_WRITE_FUNC(_stream, 1, uint8_t)
ASAN_BUS_WRITE_PTR_FUNC(multi, 1, uint8_t)
ASAN_BUS_WRITE_PTR_FUNC(multi_stream, 1, uint8_t)
ASAN_BUS_WRITE_PTR_FUNC(region, 1, uint8_t)
ASAN_BUS_WRITE_PTR_FUNC(region_stream, 1, uint8_t)
ASAN_BUS_WRITE_FUNC(, 2, uint16_t)
ASAN_BUS_WRITE_FUNC(_stream, 2, uint16_t)
ASAN_BUS_WRITE_PTR_FUNC(multi, 2, uint16_t)
ASAN_BUS_WRITE_PTR_FUNC(multi_stream, 2, uint16_t)
ASAN_BUS_WRITE_PTR_FUNC(region, 2, uint16_t)
ASAN_BUS_WRITE_PTR_FUNC(region_stream, 2, uint16_t)
ASAN_BUS_WRITE_FUNC(, 4, uint32_t)
ASAN_BUS_WRITE_FUNC(_stream, 4, uint32_t)
ASAN_BUS_WRITE_PTR_FUNC(multi, 4, uint32_t)
ASAN_BUS_WRITE_PTR_FUNC(multi_stream, 4, uint32_t)
ASAN_BUS_WRITE_PTR_FUNC(region, 4, uint32_t)
ASAN_BUS_WRITE_PTR_FUNC(region_stream, 4, uint32_t)
ASAN_BUS_WRITE_FUNC(, 8, uint64_t)
#define ASAN_BUS_SET_FUNC(func, width, type) \
void kasan_bus_space_set_##func##_##width(bus_space_tag_t tag, \
bus_space_handle_t hnd, bus_size_t offset, type value, \
bus_size_t count) \
{ \
bus_space_set_##func##_##width(tag, hnd, offset, value, \
count); \
}
ASAN_BUS_SET_FUNC(multi, 1, uint8_t)
ASAN_BUS_SET_FUNC(region, 1, uint8_t)
ASAN_BUS_SET_FUNC(multi_stream, 1, uint8_t)
ASAN_BUS_SET_FUNC(region_stream, 1, uint8_t)
ASAN_BUS_SET_FUNC(multi, 2, uint16_t)
ASAN_BUS_SET_FUNC(region, 2, uint16_t)
ASAN_BUS_SET_FUNC(multi_stream, 2, uint16_t)
ASAN_BUS_SET_FUNC(region_stream, 2, uint16_t)
ASAN_BUS_SET_FUNC(multi, 4, uint32_t)
ASAN_BUS_SET_FUNC(region, 4, uint32_t)
ASAN_BUS_SET_FUNC(multi_stream, 4, uint32_t)
ASAN_BUS_SET_FUNC(region_stream, 4, uint32_t)
/* -------------------------------------------------------------------------- */
void __asan_register_globals(struct __asan_global *, size_t);
void __asan_unregister_globals(struct __asan_global *, size_t);
void
__asan_register_globals(struct __asan_global *globals, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
kasan_mark(globals[i].beg, globals[i].size,
globals[i].size_with_redzone, KASAN_GENERIC_REDZONE);
}
}
void
__asan_unregister_globals(struct __asan_global *globals, size_t n)
{
/* never called */
}
#define ASAN_LOAD_STORE(size) \
void __asan_load##size(unsigned long); \
void __asan_load##size(unsigned long addr) \
{ \
kasan_shadow_check(addr, size, false, __RET_ADDR);\
} \
void __asan_load##size##_noabort(unsigned long); \
void __asan_load##size##_noabort(unsigned long addr) \
{ \
kasan_shadow_check(addr, size, false, __RET_ADDR);\
} \
void __asan_store##size(unsigned long); \
void __asan_store##size(unsigned long addr) \
{ \
kasan_shadow_check(addr, size, true, __RET_ADDR);\
} \
void __asan_store##size##_noabort(unsigned long); \
void __asan_store##size##_noabort(unsigned long addr) \
{ \
kasan_shadow_check(addr, size, true, __RET_ADDR);\
}
ASAN_LOAD_STORE(1);
ASAN_LOAD_STORE(2);
ASAN_LOAD_STORE(4);
ASAN_LOAD_STORE(8);
ASAN_LOAD_STORE(16);
void __asan_loadN(unsigned long, size_t);
void __asan_loadN_noabort(unsigned long, size_t);
void __asan_storeN(unsigned long, size_t);
void __asan_storeN_noabort(unsigned long, size_t);
void __asan_handle_no_return(void);
void
__asan_loadN(unsigned long addr, size_t size)
{
kasan_shadow_check(addr, size, false, __RET_ADDR);
}
void
__asan_loadN_noabort(unsigned long addr, size_t size)
{
kasan_shadow_check(addr, size, false, __RET_ADDR);
}
void
__asan_storeN(unsigned long addr, size_t size)
{
kasan_shadow_check(addr, size, true, __RET_ADDR);
}
void
__asan_storeN_noabort(unsigned long addr, size_t size)
{
kasan_shadow_check(addr, size, true, __RET_ADDR);
}
void
__asan_handle_no_return(void)
{
/* nothing */
}
#define ASAN_SET_SHADOW(byte) \
void __asan_set_shadow_##byte(void *, size_t); \
void __asan_set_shadow_##byte(void *addr, size_t size) \
{ \
__builtin_memset((void *)addr, 0x##byte, size); \
}
ASAN_SET_SHADOW(00);
ASAN_SET_SHADOW(f1);
ASAN_SET_SHADOW(f2);
ASAN_SET_SHADOW(f3);
ASAN_SET_SHADOW(f5);
ASAN_SET_SHADOW(f8);
void __asan_poison_stack_memory(const void *, size_t);
void __asan_unpoison_stack_memory(const void *, size_t);
void
__asan_poison_stack_memory(const void *addr, size_t size)
{
size = roundup(size, KASAN_SHADOW_SCALE);
kasan_shadow_Nbyte_fill(addr, size, KASAN_USE_AFTER_SCOPE);
}
void
__asan_unpoison_stack_memory(const void *addr, size_t size)
{
kasan_shadow_Nbyte_markvalid(addr, size);
}
void __asan_alloca_poison(const void *, size_t);
void __asan_allocas_unpoison(const void *, const void *);
void
__asan_alloca_poison(const void *addr, size_t size)
{
const void *l, *r;
KASSERT((vm_offset_t)addr % KASAN_ALLOCA_SCALE_SIZE == 0,
("%s: invalid address %p", __func__, addr));
l = (const uint8_t *)addr - KASAN_ALLOCA_SCALE_SIZE;
r = (const uint8_t *)addr + roundup(size, KASAN_ALLOCA_SCALE_SIZE);
kasan_shadow_Nbyte_fill(l, KASAN_ALLOCA_SCALE_SIZE, KASAN_STACK_LEFT);
kasan_mark(addr, size, roundup(size, KASAN_ALLOCA_SCALE_SIZE),
KASAN_STACK_MID);
kasan_shadow_Nbyte_fill(r, KASAN_ALLOCA_SCALE_SIZE, KASAN_STACK_RIGHT);
}
void
__asan_allocas_unpoison(const void *stkbegin, const void *stkend)
{
size_t size;
if (__predict_false(!stkbegin))
return;
if (__predict_false((uintptr_t)stkbegin > (uintptr_t)stkend))
return;
size = (uintptr_t)stkend - (uintptr_t)stkbegin;
kasan_shadow_Nbyte_fill(stkbegin, size, 0);
}
void __asan_poison_memory_region(const void *addr, size_t size);
void __asan_unpoison_memory_region(const void *addr, size_t size);
void
__asan_poison_memory_region(const void *addr, size_t size)
{
}
void
__asan_unpoison_memory_region(const void *addr, size_t size)
{
}