mirror of
https://github.com/apache/nuttx.git
synced 2026-06-02 17:48:54 +08:00
kasan: Implementation of Kasan based on software tags.
Currently, only aarch64 is supported Signed-off-by: wangmingrong <wangmingrong@xiaomi.com>
This commit is contained in:
@@ -19,6 +19,7 @@ Guides
|
|||||||
customapps.rst
|
customapps.rst
|
||||||
citests.rst
|
citests.rst
|
||||||
zerolatencyinterrupts.rst
|
zerolatencyinterrupts.rst
|
||||||
|
kasan.rst
|
||||||
nestedinterrupts.rst
|
nestedinterrupts.rst
|
||||||
cortexmhardfaults.rst
|
cortexmhardfaults.rst
|
||||||
coredump.rst
|
coredump.rst
|
||||||
|
|||||||
@@ -8,17 +8,23 @@ Overview
|
|||||||
Kernel Address Sanitizer (KASAN) is a dynamic memory safety error detector
|
Kernel Address Sanitizer (KASAN) is a dynamic memory safety error detector
|
||||||
designed to find out-of-bounds and use-after-free bugs.
|
designed to find out-of-bounds and use-after-free bugs.
|
||||||
|
|
||||||
The current version of NuttX has one modes:
|
The current version of NuttX has two modes:
|
||||||
|
|
||||||
1. Generic KASAN
|
1. Generic KASAN
|
||||||
|
2. Software Tag-Based KASAN
|
||||||
|
|
||||||
Generic KASAN, enabled with CONFIG_MM_KASAN, is the mode intended for
|
Generic KASAN, enabled with CONFIG_MM_KASAN_GENERIC, is the mode intended for
|
||||||
debugging, similar to linux user level ASan. This mode is supported on many CPU
|
debugging, similar to linux user level ASan. This mode is supported on many CPU
|
||||||
architectures, but it has significant performance and memory overheads.
|
architectures, but it has significant performance and memory overheads.
|
||||||
The current NuttX Generic KASAN can support memory out of bounds detection
|
The current NuttX Generic KASAN can support memory out of bounds detection
|
||||||
allocated by the default NuttX heap allocator,which depends on CONFIG_MM_DEFAULT_MANAGER
|
allocated by the default NuttX heap allocator,which depends on CONFIG_MM_DEFAULT_MANAGER
|
||||||
or CONFIG_MM_TLSF_MANAGER, and detection of out of bounds with global variables.
|
or CONFIG_MM_TLSF_MANAGER, and detection of out of bounds with global variables.
|
||||||
|
|
||||||
|
Software Tag-Based KASAN or SW_TAGS KASAN, enabled with CONFIG_MM_KASAN_SW_TAGS,
|
||||||
|
can be used for both debugging, This mode is only supported for arm64,
|
||||||
|
but its moderate memory overhead allows using it for testing on
|
||||||
|
memory-restricted devices with real workloads.
|
||||||
|
|
||||||
Support
|
Support
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -27,20 +33,33 @@ Architectures
|
|||||||
|
|
||||||
Generic KASAN is supported on x86_64, arm, arm64, riscv, xtensa and so on.
|
Generic KASAN is supported on x86_64, arm, arm64, riscv, xtensa and so on.
|
||||||
|
|
||||||
|
Software Tag-Based KASAN modes are supported only on arm64.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
To enable Generic KASAN, configure the kernel with::
|
To enable Generic KASAN, configure the kernel with::
|
||||||
|
|
||||||
CONFIG_MM_KASAN=y
|
CONFIG_MM_KASAN=y
|
||||||
CONFIG_MM_KASAN_ALL=y
|
CONFIG_MM_KASAN_ALL=y
|
||||||
|
CONFIG_MM_KASAN_GENERIC=y
|
||||||
|
|
||||||
If you want to enable global variable out of bounds detection,
|
If you want to enable global variable out of bounds detection,
|
||||||
you can add configurations based on the above::
|
you can add configurations based on the above::
|
||||||
|
|
||||||
CONFIG_MM_KASAN_GLOBAL=y
|
CONFIG_MM_KASAN_GLOBAL=y
|
||||||
|
|
||||||
|
To enable Software Tag-Based KASAN, configure the kernel with::
|
||||||
|
|
||||||
|
CONFIG_MM_KASAN=y
|
||||||
|
CONFIG_MM_KASAN_ALL=y
|
||||||
|
CONFIG_MM_KASAN_SW_TAGS=y
|
||||||
|
|
||||||
Implementation details
|
Implementation details
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
Generic KASAN:
|
||||||
|
|
||||||
Compile with param -fsanitize=kernel-address,
|
Compile with param -fsanitize=kernel-address,
|
||||||
Compile-time instrumentation is used to insert memory access checks. Compiler
|
Compile-time instrumentation is used to insert memory access checks. Compiler
|
||||||
inserts function calls (``__asan_load*(addr)``, ``__asan_store*(addr)``) before
|
inserts function calls (``__asan_load*(addr)``, ``__asan_store*(addr)``) before
|
||||||
@@ -94,6 +113,26 @@ The data generated by the compiler will be placed in a non-existent memory block
|
|||||||
After the compilation is completed, this segment will be deleted
|
After the compilation is completed, this segment will be deleted
|
||||||
and will not be copied to the bin file of the final burned board.
|
and will not be copied to the bin file of the final burned board.
|
||||||
|
|
||||||
|
Software Tag-Based KASAN:
|
||||||
|
|
||||||
|
Software Tag-Based KASAN uses a software memory tagging approach to checking
|
||||||
|
access validity. It is currently only implemented for the arm64 architecture.
|
||||||
|
|
||||||
|
Software Tag-Based KASAN uses the Top Byte Ignore (TBI) feature of arm64 CPUs
|
||||||
|
to store a pointer tag in the top byte of kernel pointers. It uses shadow memory
|
||||||
|
to store memory tags associated with each heap allocated memory cell (therefore, it
|
||||||
|
dedicates 1/8 th of the kernel memory for shadow memory).
|
||||||
|
|
||||||
|
On each memory allocation, Software Tag-Based KASAN generates a random tag, tags
|
||||||
|
the allocated memory with this tag, and embeds the same tag into the returned
|
||||||
|
pointer.
|
||||||
|
|
||||||
|
Software Tag-Based KASAN uses compile-time instrumentation to insert checks
|
||||||
|
before each memory access. These checks make sure that the tag of the memory
|
||||||
|
that is being accessed is equal to the tag of the pointer that is used to access
|
||||||
|
this memory. In case of a tag mismatch, Software Tag-Based KASAN prints a bug
|
||||||
|
report.
|
||||||
|
|
||||||
For developers
|
For developers
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@@ -102,4 +141,6 @@ Ignoring accesses
|
|||||||
|
|
||||||
If you want the module you are writing to not be inserted by the compiler,
|
If you want the module you are writing to not be inserted by the compiler,
|
||||||
you can add the option 'CFLAGS += -fno-sanitize=kernel-address' to a single module.
|
you can add the option 'CFLAGS += -fno-sanitize=kernel-address' to a single module.
|
||||||
If it is a file, you can write it this way, special_file.o: CFLAGS = -fno-sanitize=kernel-address
|
If it is a file, you can write it this way,
|
||||||
|
special_file.o: CFLAGS = -fno-sanitize=kernel-address
|
||||||
|
or : special_file.o: CFLAGS = -fno-sanitize=kernel-hwaddress
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ ifeq ($(CONFIG_MM_UBSAN_TRAP_ON_ERROR),y)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_MM_KASAN_ALL),y)
|
ifeq ($(CONFIG_MM_KASAN_ALL),y)
|
||||||
ARCHOPTIMIZATION += -fsanitize=kernel-address
|
ifeq ($(CONFIG_MM_KASAN_GENERIC),y)
|
||||||
|
ARCHOPTIMIZATION += -fsanitize=kernel-address
|
||||||
|
else ifeq ($(CONFIG_MM_KASAN_SW_TAGS),y)
|
||||||
|
ARCHOPTIMIZATION += -fsanitize=kernel-hwaddress
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_MM_KASAN_GLOBAL),y)
|
ifeq ($(CONFIG_MM_KASAN_GLOBAL),y)
|
||||||
|
|||||||
@@ -153,6 +153,12 @@
|
|||||||
#define TCR_PS_BITS TCR_PS_BITS_4GB
|
#define TCR_PS_BITS TCR_PS_BITS_4GB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_MM_KASAN_SW_TAGS
|
||||||
|
#define TCR_KASAN_SW_FLAGS (TCR_TBI0 | TCR_TBI1 | TCR_ASID_8)
|
||||||
|
#else
|
||||||
|
#define TCR_KASAN_SW_FLAGS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Data
|
* Private Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -254,7 +260,8 @@ static uint64_t get_tcr(int el)
|
|||||||
* inner shareable
|
* inner shareable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
tcr |= TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA;
|
tcr |= TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA |
|
||||||
|
TCR_IRGN_WBWA | TCR_KASAN_SW_FLAGS;
|
||||||
|
|
||||||
return tcr;
|
return tcr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,12 @@
|
|||||||
#define TCR_TG0_16K (2ULL << 14)
|
#define TCR_TG0_16K (2ULL << 14)
|
||||||
#define TCR_EPD1_DISABLE (1ULL << 23)
|
#define TCR_EPD1_DISABLE (1ULL << 23)
|
||||||
|
|
||||||
|
#define TCR_AS_SHIFT 36U
|
||||||
|
#define TCR_ASID_8 (0ULL << TCR_AS_SHIFT)
|
||||||
|
#define TCR_ASID_16 (1ULL << TCR_AS_SHIFT)
|
||||||
|
#define TCR_TBI0 (1ULL << 37)
|
||||||
|
#define TCR_TBI1 (1ULL << 38)
|
||||||
|
|
||||||
#define TCR_PS_BITS_4GB 0x0ULL
|
#define TCR_PS_BITS_4GB 0x0ULL
|
||||||
#define TCR_PS_BITS_64GB 0x1ULL
|
#define TCR_PS_BITS_64GB 0x1ULL
|
||||||
#define TCR_PS_BITS_1TB 0x2ULL
|
#define TCR_PS_BITS_1TB 0x2ULL
|
||||||
|
|||||||
@@ -37,9 +37,10 @@
|
|||||||
#ifndef CONFIG_MM_KASAN
|
#ifndef CONFIG_MM_KASAN
|
||||||
# define kasan_is_poisoned(addr, size) false
|
# define kasan_is_poisoned(addr, size) false
|
||||||
# define kasan_poison(addr, size)
|
# define kasan_poison(addr, size)
|
||||||
# define kasan_unpoison(addr, size)
|
# define kasan_unpoison(addr, size) addr
|
||||||
# define kasan_register(addr, size)
|
# define kasan_register(addr, size)
|
||||||
# define kasan_init_early()
|
# define kasan_init_early()
|
||||||
|
# define kasan_reset_tag(addr) addr
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -99,11 +100,11 @@ void kasan_poison(FAR const void *addr, size_t size);
|
|||||||
* size - range size
|
* size - range size
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* None.
|
* Return tagged address
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void kasan_unpoison(FAR const void *addr, size_t size);
|
FAR void *kasan_unpoison(FAR const void *addr, size_t size);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: kasan_register
|
* Name: kasan_register
|
||||||
@@ -143,6 +144,19 @@ void kasan_register(FAR void *addr, FAR size_t *size);
|
|||||||
|
|
||||||
void kasan_init_early(void);
|
void kasan_init_early(void);
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: kasan_reset_tag
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* addr - The address of the memory to reset the tag.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* Unlabeled address
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
FAR void *kasan_reset_tag(FAR const void *addr);
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-1
@@ -258,6 +258,25 @@ config MM_KASAN
|
|||||||
bugs in native code. After turn on this option, Please
|
bugs in native code. After turn on this option, Please
|
||||||
add -fsanitize=kernel-address to CFLAGS/CXXFLAGS too.
|
add -fsanitize=kernel-address to CFLAGS/CXXFLAGS too.
|
||||||
|
|
||||||
|
if MM_KASAN
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "KAsan Mode"
|
||||||
|
default MM_KASAN_GENERIC
|
||||||
|
|
||||||
|
config MM_KASAN_GENERIC
|
||||||
|
bool "KAsan generic mode"
|
||||||
|
---help---
|
||||||
|
KASan generic mode that does not require hardware support at all
|
||||||
|
|
||||||
|
config MM_KASAN_SW_TAGS
|
||||||
|
bool "KAsan SW tags"
|
||||||
|
depends on ARCH_ARM64
|
||||||
|
---help---
|
||||||
|
KAsan based on software tags
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
config MM_KASAN_ALL
|
config MM_KASAN_ALL
|
||||||
bool "Enable KASan for the entire image"
|
bool "Enable KASan for the entire image"
|
||||||
depends on MM_KASAN
|
depends on MM_KASAN
|
||||||
@@ -288,7 +307,7 @@ config MM_KASAN_DISABLE_WRITES_CHECK
|
|||||||
|
|
||||||
config MM_KASAN_GLOBAL
|
config MM_KASAN_GLOBAL
|
||||||
bool "Enable global data check"
|
bool "Enable global data check"
|
||||||
depends on MM_KASAN
|
depends on MM_KASAN_GENERIC
|
||||||
default n
|
default n
|
||||||
---help---
|
---help---
|
||||||
This option enables KASan global data check.
|
This option enables KASan global data check.
|
||||||
|
|||||||
+12
-4
@@ -23,16 +23,24 @@
|
|||||||
CSRCS += hook.c
|
CSRCS += hook.c
|
||||||
|
|
||||||
ifeq ($(CONFIG_MM_KASAN),y)
|
ifeq ($(CONFIG_MM_KASAN),y)
|
||||||
CSRCS += generic.c
|
|
||||||
|
|
||||||
# Disable kernel-address in mm subsystem
|
|
||||||
|
|
||||||
ifeq ($(CONFIG_ARCH_TOOLCHAIN_GNU),y)
|
ifeq ($(CONFIG_ARCH_TOOLCHAIN_GNU),y)
|
||||||
CFLAGS += -fno-sanitize=kernel-address
|
|
||||||
|
ifeq ($(CONFIG_MM_KASAN_GENERIC),y)
|
||||||
|
CSRCS += generic.c
|
||||||
|
CFLAGS += -fno-sanitize=kernel-address
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_MM_KASAN_SW_TAGS),y)
|
||||||
|
CSRCS += sw_tags.c
|
||||||
|
CFLAGS += -fno-sanitize=kernel-hwaddress
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_LTO_NONE),n)
|
ifeq ($(CONFIG_LTO_NONE),n)
|
||||||
CFLAGS += -fno-lto
|
CFLAGS += -fno-lto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Add the core heap directory to the build
|
# Add the core heap directory to the build
|
||||||
|
|||||||
+8
-2
@@ -139,10 +139,10 @@ static void kasan_set_poison(FAR const void *addr, size_t size,
|
|||||||
bool poisoned)
|
bool poisoned)
|
||||||
{
|
{
|
||||||
FAR uintptr_t *p;
|
FAR uintptr_t *p;
|
||||||
|
irqstate_t flags;
|
||||||
unsigned int bit;
|
unsigned int bit;
|
||||||
unsigned int nbit;
|
unsigned int nbit;
|
||||||
uintptr_t mask;
|
uintptr_t mask;
|
||||||
int flags;
|
|
||||||
|
|
||||||
p = kasan_mem_to_shadow(addr, size, &bit);
|
p = kasan_mem_to_shadow(addr, size, &bit);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
@@ -193,6 +193,11 @@ static void kasan_set_poison(FAR const void *addr, size_t size,
|
|||||||
* Public Functions
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
FAR void *kasan_reset_tag(FAR const void *addr)
|
||||||
|
{
|
||||||
|
return (FAR void *)addr;
|
||||||
|
}
|
||||||
|
|
||||||
bool kasan_is_poisoned(FAR const void *addr, size_t size)
|
bool kasan_is_poisoned(FAR const void *addr, size_t size)
|
||||||
{
|
{
|
||||||
FAR uintptr_t *p;
|
FAR uintptr_t *p;
|
||||||
@@ -207,9 +212,10 @@ void kasan_poison(FAR const void *addr, size_t size)
|
|||||||
kasan_set_poison(addr, size, true);
|
kasan_set_poison(addr, size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kasan_unpoison(FAR const void *addr, size_t size)
|
FAR void *kasan_unpoison(FAR const void *addr, size_t size)
|
||||||
{
|
{
|
||||||
kasan_set_poison(addr, size, false);
|
kasan_set_poison(addr, size, false);
|
||||||
|
return (FAR void *)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kasan_register(FAR void *addr, FAR size_t *size)
|
void kasan_register(FAR void *addr, FAR size_t *size)
|
||||||
|
|||||||
+59
-25
@@ -31,6 +31,46 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Pre-processor Definitions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#define DEFINE_ASAN_LOAD_STORE(size) \
|
||||||
|
void __asan_report_load##size##_noabort(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_report(addr, size, false, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __asan_report_store##size##_noabort(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_report(addr, size, true, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __asan_load##size##_noabort(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, false, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __asan_store##size##_noabort(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, true, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __asan_load##size(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, false, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __asan_store##size(FAR void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, true, return_address(0)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_HWASAN_LOAD_STORE(size) \
|
||||||
|
void __hwasan_load##size##_noabort(void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, false, return_address(0)); \
|
||||||
|
} \
|
||||||
|
void __hwasan_store##size##_noabort(void *addr) \
|
||||||
|
{ \
|
||||||
|
kasan_check_report(addr, size, true, return_address(0)); \
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Functions
|
* Private Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -181,34 +221,28 @@ void __asan_storeN(FAR void *addr, size_t size)
|
|||||||
kasan_check_report(addr, size, true, return_address(0));
|
kasan_check_report(addr, size, true, return_address(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFINE_ASAN_LOAD_STORE(size) \
|
/* Generic KASan will instrument the following functions */
|
||||||
void __asan_report_load##size##_noabort(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_report(addr, size, false, return_address(0)); \
|
|
||||||
} \
|
|
||||||
void __asan_report_store##size##_noabort(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_report(addr, size, true, return_address(0)); \
|
|
||||||
} \
|
|
||||||
void __asan_load##size##_noabort(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_check_report(addr, size, false, return_address(0)); \
|
|
||||||
} \
|
|
||||||
void __asan_store##size##_noabort(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_check_report(addr, size, true, return_address(0)); \
|
|
||||||
} \
|
|
||||||
void __asan_load##size(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_check_report(addr, size, false, return_address(0)); \
|
|
||||||
} \
|
|
||||||
void __asan_store##size(FAR void *addr) \
|
|
||||||
{ \
|
|
||||||
kasan_check_report(addr, size, true, return_address(0)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_ASAN_LOAD_STORE(1)
|
DEFINE_ASAN_LOAD_STORE(1)
|
||||||
DEFINE_ASAN_LOAD_STORE(2)
|
DEFINE_ASAN_LOAD_STORE(2)
|
||||||
DEFINE_ASAN_LOAD_STORE(4)
|
DEFINE_ASAN_LOAD_STORE(4)
|
||||||
DEFINE_ASAN_LOAD_STORE(8)
|
DEFINE_ASAN_LOAD_STORE(8)
|
||||||
DEFINE_ASAN_LOAD_STORE(16)
|
DEFINE_ASAN_LOAD_STORE(16)
|
||||||
|
|
||||||
|
/* Soft tags KASan will instrument the following functions */
|
||||||
|
|
||||||
|
void __hwasan_loadN_noabort(FAR void *addr, size_t size)
|
||||||
|
{
|
||||||
|
kasan_check_report(addr, size, false, return_address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void __hwasan_storeN_noabort(FAR void *addr, size_t size)
|
||||||
|
{
|
||||||
|
kasan_check_report(addr, size, true, return_address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_HWASAN_LOAD_STORE(1)
|
||||||
|
DEFINE_HWASAN_LOAD_STORE(2)
|
||||||
|
DEFINE_HWASAN_LOAD_STORE(4)
|
||||||
|
DEFINE_HWASAN_LOAD_STORE(8)
|
||||||
|
DEFINE_HWASAN_LOAD_STORE(16)
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* mm/kasan/sw_tags.c
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership. The
|
||||||
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the
|
||||||
|
* License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Included Files
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include <nuttx/mm/kasan.h>
|
||||||
|
#include <nuttx/spinlock.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Pre-processor Definitions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#define KASAN_TAG_SHIFT 56
|
||||||
|
|
||||||
|
#define kasan_get_tag(addr) \
|
||||||
|
((uint8_t)((uint64_t)(addr) >> KASAN_TAG_SHIFT))
|
||||||
|
|
||||||
|
#define kasan_set_tag(addr, tag) \
|
||||||
|
(FAR void *)((((uint64_t)(addr)) & ~((uint64_t)0xff << KASAN_TAG_SHIFT)) | \
|
||||||
|
(((uint64_t)(tag)) << KASAN_TAG_SHIFT))
|
||||||
|
|
||||||
|
#define kasan_random_tag() (rand() % ((1 << (64 - KASAN_TAG_SHIFT)) - 1))
|
||||||
|
|
||||||
|
#define KASAN_SHADOW_SCALE (sizeof(uintptr_t))
|
||||||
|
|
||||||
|
#define KASAN_SHADOW_SIZE(size) \
|
||||||
|
((size) + KASAN_SHADOW_SCALE - 1) / KASAN_SHADOW_SCALE
|
||||||
|
#define KASAN_REGION_SIZE(size) \
|
||||||
|
(sizeof(struct kasan_region_s) + KASAN_SHADOW_SIZE(size))
|
||||||
|
|
||||||
|
#define KASAN_INIT_VALUE 0xdeadcafe
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Private Types
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
struct kasan_region_s
|
||||||
|
{
|
||||||
|
FAR struct kasan_region_s *next;
|
||||||
|
uintptr_t begin;
|
||||||
|
uintptr_t end;
|
||||||
|
uint8_t shadow[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Private Data
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static spinlock_t g_lock;
|
||||||
|
static FAR struct kasan_region_s *g_region;
|
||||||
|
static uint32_t g_region_init;
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Private Functions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static FAR uint8_t *kasan_mem_to_shadow(FAR const void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
FAR struct kasan_region_s *region;
|
||||||
|
uintptr_t addr;
|
||||||
|
|
||||||
|
addr = (uintptr_t)kasan_reset_tag(ptr);
|
||||||
|
if (size == 0 || g_region_init != KASAN_INIT_VALUE)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (region = g_region; region != NULL; region = region->next)
|
||||||
|
{
|
||||||
|
if (addr >= region->begin && addr < region->end)
|
||||||
|
{
|
||||||
|
DEBUGASSERT(addr + size <= region->end);
|
||||||
|
addr -= region->begin;
|
||||||
|
return ®ion->shadow[addr / KASAN_SHADOW_SCALE];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kasan_set_poison(FAR const void *addr,
|
||||||
|
size_t size, uint8_t value)
|
||||||
|
{
|
||||||
|
irqstate_t flags;
|
||||||
|
FAR uint8_t *p;
|
||||||
|
|
||||||
|
addr = kasan_reset_tag(addr);
|
||||||
|
p = kasan_mem_to_shadow(addr, size);
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = KASAN_SHADOW_SIZE(size);
|
||||||
|
flags = spin_lock_irqsave(&g_lock);
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
p[size] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Public Functions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
FAR void *kasan_reset_tag(FAR const void *addr)
|
||||||
|
{
|
||||||
|
return (FAR void *)
|
||||||
|
(((uint64_t)(addr)) & ~((uint64_t)0xff << KASAN_TAG_SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kasan_is_poisoned(FAR const void *addr, size_t size)
|
||||||
|
{
|
||||||
|
FAR uint8_t *p;
|
||||||
|
uint8_t tag;
|
||||||
|
|
||||||
|
tag = kasan_get_tag(addr);
|
||||||
|
p = kasan_mem_to_shadow(addr, size);
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = KASAN_SHADOW_SIZE(size);
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
if (p[size] != tag)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kasan_poison(FAR const void *addr, size_t size)
|
||||||
|
{
|
||||||
|
kasan_set_poison(addr, size, 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
FAR void *kasan_unpoison(FAR const void *addr, size_t size)
|
||||||
|
{
|
||||||
|
uint8_t tag = kasan_random_tag();
|
||||||
|
|
||||||
|
kasan_set_poison(addr, size, tag);
|
||||||
|
return kasan_set_tag(addr, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kasan_register(FAR void *addr, FAR size_t *size)
|
||||||
|
{
|
||||||
|
FAR struct kasan_region_s *region;
|
||||||
|
irqstate_t flags;
|
||||||
|
|
||||||
|
region = (FAR struct kasan_region_s *)
|
||||||
|
((FAR char *)addr + *size - KASAN_REGION_SIZE(*size));
|
||||||
|
|
||||||
|
region->begin = (uintptr_t)addr;
|
||||||
|
region->end = region->begin + *size;
|
||||||
|
|
||||||
|
flags = spin_lock_irqsave(&g_lock);
|
||||||
|
region->next = g_region;
|
||||||
|
g_region = region;
|
||||||
|
spin_unlock_irqrestore(&g_lock, flags);
|
||||||
|
|
||||||
|
g_region_init = KASAN_INIT_VALUE;
|
||||||
|
kasan_poison(addr, *size);
|
||||||
|
*size -= KASAN_REGION_SIZE(*size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kasan_init_early(void)
|
||||||
|
{
|
||||||
|
g_region_init = 0;
|
||||||
|
}
|
||||||
@@ -406,7 +406,7 @@ retry:
|
|||||||
|
|
||||||
pool->nalloc++;
|
pool->nalloc++;
|
||||||
spin_unlock_irqrestore(&pool->lock, flags);
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
kasan_unpoison(blk, pool->blocksize);
|
blk = kasan_unpoison(blk, pool->blocksize);
|
||||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||||
memset(blk, MM_ALLOC_MAGIC, pool->blocksize);
|
memset(blk, MM_ALLOC_MAGIC, pool->blocksize);
|
||||||
#endif
|
#endif
|
||||||
@@ -648,7 +648,7 @@ int mempool_deinit(FAR struct mempool_s *pool)
|
|||||||
{
|
{
|
||||||
blk = (FAR sq_entry_t *)((FAR char *)blk - count * blocksize);
|
blk = (FAR sq_entry_t *)((FAR char *)blk - count * blocksize);
|
||||||
|
|
||||||
kasan_unpoison(blk, count * blocksize + sizeof(sq_entry_t));
|
blk = kasan_unpoison(blk, count * blocksize + sizeof(sq_entry_t));
|
||||||
pool->free(pool, blk);
|
pool->free(pool, blk);
|
||||||
if (pool->expandsize >= blocksize + sizeof(sq_entry_t))
|
if (pool->expandsize >= blocksize + sizeof(sq_entry_t))
|
||||||
{
|
{
|
||||||
@@ -658,8 +658,8 @@ int mempool_deinit(FAR struct mempool_s *pool)
|
|||||||
|
|
||||||
if (pool->ibase)
|
if (pool->ibase)
|
||||||
{
|
{
|
||||||
kasan_unpoison(pool->ibase,
|
pool->ibase = kasan_unpoison(pool->ibase,
|
||||||
pool->interruptsize / blocksize * blocksize);
|
pool->interruptsize / blocksize * blocksize);
|
||||||
pool->free(pool, pool->ibase);
|
pool->free(pool, pool->ibase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
#include <nuttx/mm/mm.h>
|
#include <nuttx/mm/mm.h>
|
||||||
|
#include <nuttx/mm/kasan.h>
|
||||||
|
|
||||||
#include "mm_heap/mm.h"
|
#include "mm_heap/mm.h"
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@
|
|||||||
|
|
||||||
bool mm_heapmember(FAR struct mm_heap_s *heap, FAR void *mem)
|
bool mm_heapmember(FAR struct mm_heap_s *heap, FAR void *mem)
|
||||||
{
|
{
|
||||||
|
mem = kasan_reset_tag(mem);
|
||||||
#if CONFIG_MM_REGIONS > 1
|
#if CONFIG_MM_REGIONS > 1
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ FAR void *mm_malloc(FAR struct mm_heap_s *heap, size_t size)
|
|||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
MM_ADD_BACKTRACE(heap, node);
|
MM_ADD_BACKTRACE(heap, node);
|
||||||
kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
ret = kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
||||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||||
memset(ret, MM_ALLOC_MAGIC, alignsize - MM_ALLOCNODE_OVERHEAD);
|
memset(ret, MM_ALLOC_MAGIC, alignsize - MM_ALLOCNODE_OVERHEAD);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -277,8 +277,10 @@ FAR void *mm_memalign(FAR struct mm_heap_s *heap, size_t alignment,
|
|||||||
|
|
||||||
MM_ADD_BACKTRACE(heap, node);
|
MM_ADD_BACKTRACE(heap, node);
|
||||||
|
|
||||||
kasan_unpoison((FAR void *)alignedchunk,
|
alignedchunk = (uintptr_t)kasan_unpoison
|
||||||
mm_malloc_size(heap, (FAR void *)alignedchunk));
|
((FAR const void *)alignedchunk,
|
||||||
|
mm_malloc_size(heap,
|
||||||
|
(FAR void *)alignedchunk));
|
||||||
|
|
||||||
DEBUGASSERT(alignedchunk % alignment == 0);
|
DEBUGASSERT(alignedchunk % alignment == 0);
|
||||||
return (FAR void *)alignedchunk;
|
return (FAR void *)alignedchunk;
|
||||||
|
|||||||
@@ -385,8 +385,8 @@ FAR void *mm_realloc(FAR struct mm_heap_s *heap, FAR void *oldmem,
|
|||||||
mm_unlock(heap);
|
mm_unlock(heap);
|
||||||
MM_ADD_BACKTRACE(heap, (FAR char *)newmem - MM_SIZEOF_ALLOCNODE);
|
MM_ADD_BACKTRACE(heap, (FAR char *)newmem - MM_SIZEOF_ALLOCNODE);
|
||||||
|
|
||||||
kasan_unpoison(newmem, mm_malloc_size(heap, newmem));
|
newmem = kasan_unpoison(newmem, mm_malloc_size(heap, newmem));
|
||||||
if (newmem != oldmem)
|
if (kasan_reset_tag(newmem) != kasan_reset_tag(oldmem))
|
||||||
{
|
{
|
||||||
/* Now we have to move the user contents 'down' in memory. memcpy
|
/* Now we have to move the user contents 'down' in memory. memcpy
|
||||||
* should be safe for this.
|
* should be safe for this.
|
||||||
|
|||||||
+2
-2
@@ -1183,7 +1183,7 @@ FAR void *mm_malloc(FAR struct mm_heap_s *heap, size_t size)
|
|||||||
|
|
||||||
memdump_backtrace(heap, buf);
|
memdump_backtrace(heap, buf);
|
||||||
#endif
|
#endif
|
||||||
kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
ret = kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
||||||
|
|
||||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||||
memset(ret, 0xaa, nodesize);
|
memset(ret, 0xaa, nodesize);
|
||||||
@@ -1260,7 +1260,7 @@ FAR void *mm_memalign(FAR struct mm_heap_s *heap, size_t alignment,
|
|||||||
|
|
||||||
memdump_backtrace(heap, buf);
|
memdump_backtrace(heap, buf);
|
||||||
#endif
|
#endif
|
||||||
kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
ret = kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_MM_FREE_DELAYCOUNT_MAX > 0
|
#if CONFIG_MM_FREE_DELAYCOUNT_MAX > 0
|
||||||
|
|||||||
Reference in New Issue
Block a user