mirror of
https://github.com/apache/nuttx.git
synced 2026-06-05 07:12: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:
+20
-1
@@ -258,6 +258,25 @@ config MM_KASAN
|
||||
bugs in native code. After turn on this option, Please
|
||||
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
|
||||
bool "Enable KASan for the entire image"
|
||||
depends on MM_KASAN
|
||||
@@ -288,7 +307,7 @@ config MM_KASAN_DISABLE_WRITES_CHECK
|
||||
|
||||
config MM_KASAN_GLOBAL
|
||||
bool "Enable global data check"
|
||||
depends on MM_KASAN
|
||||
depends on MM_KASAN_GENERIC
|
||||
default n
|
||||
---help---
|
||||
This option enables KASan global data check.
|
||||
|
||||
+12
-4
@@ -23,16 +23,24 @@
|
||||
CSRCS += hook.c
|
||||
|
||||
ifeq ($(CONFIG_MM_KASAN),y)
|
||||
CSRCS += generic.c
|
||||
|
||||
# Disable kernel-address in mm subsystem
|
||||
|
||||
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)
|
||||
CFLAGS += -fno-lto
|
||||
endif
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
# 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)
|
||||
{
|
||||
FAR uintptr_t *p;
|
||||
irqstate_t flags;
|
||||
unsigned int bit;
|
||||
unsigned int nbit;
|
||||
uintptr_t mask;
|
||||
int flags;
|
||||
|
||||
p = kasan_mem_to_shadow(addr, size, &bit);
|
||||
if (p == NULL)
|
||||
@@ -193,6 +193,11 @@ static void kasan_set_poison(FAR const void *addr, size_t size,
|
||||
* 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)
|
||||
{
|
||||
FAR uintptr_t *p;
|
||||
@@ -207,9 +212,10 @@ void kasan_poison(FAR const void *addr, size_t size)
|
||||
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);
|
||||
return (FAR void *)addr;
|
||||
}
|
||||
|
||||
void kasan_register(FAR void *addr, FAR size_t *size)
|
||||
|
||||
+59
-25
@@ -31,6 +31,46 @@
|
||||
#include <stdint.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
|
||||
****************************************************************************/
|
||||
@@ -181,34 +221,28 @@ void __asan_storeN(FAR void *addr, size_t size)
|
||||
kasan_check_report(addr, size, true, return_address(0));
|
||||
}
|
||||
|
||||
#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)); \
|
||||
}
|
||||
/* Generic KASan will instrument the following functions */
|
||||
|
||||
DEFINE_ASAN_LOAD_STORE(1)
|
||||
DEFINE_ASAN_LOAD_STORE(2)
|
||||
DEFINE_ASAN_LOAD_STORE(4)
|
||||
DEFINE_ASAN_LOAD_STORE(8)
|
||||
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++;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
kasan_unpoison(blk, pool->blocksize);
|
||||
blk = kasan_unpoison(blk, pool->blocksize);
|
||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||
memset(blk, MM_ALLOC_MAGIC, pool->blocksize);
|
||||
#endif
|
||||
@@ -648,7 +648,7 @@ int mempool_deinit(FAR struct mempool_s *pool)
|
||||
{
|
||||
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);
|
||||
if (pool->expandsize >= blocksize + sizeof(sq_entry_t))
|
||||
{
|
||||
@@ -658,8 +658,8 @@ int mempool_deinit(FAR struct mempool_s *pool)
|
||||
|
||||
if (pool->ibase)
|
||||
{
|
||||
kasan_unpoison(pool->ibase,
|
||||
pool->interruptsize / blocksize * blocksize);
|
||||
pool->ibase = kasan_unpoison(pool->ibase,
|
||||
pool->interruptsize / blocksize * blocksize);
|
||||
pool->free(pool, pool->ibase);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/mm/mm.h>
|
||||
#include <nuttx/mm/kasan.h>
|
||||
|
||||
#include "mm_heap/mm.h"
|
||||
|
||||
@@ -56,6 +57,7 @@
|
||||
|
||||
bool mm_heapmember(FAR struct mm_heap_s *heap, FAR void *mem)
|
||||
{
|
||||
mem = kasan_reset_tag(mem);
|
||||
#if CONFIG_MM_REGIONS > 1
|
||||
int i;
|
||||
|
||||
|
||||
@@ -324,7 +324,7 @@ FAR void *mm_malloc(FAR struct mm_heap_s *heap, size_t size)
|
||||
if (ret)
|
||||
{
|
||||
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
|
||||
memset(ret, MM_ALLOC_MAGIC, alignsize - MM_ALLOCNODE_OVERHEAD);
|
||||
#endif
|
||||
|
||||
@@ -277,8 +277,10 @@ FAR void *mm_memalign(FAR struct mm_heap_s *heap, size_t alignment,
|
||||
|
||||
MM_ADD_BACKTRACE(heap, node);
|
||||
|
||||
kasan_unpoison((FAR void *)alignedchunk,
|
||||
mm_malloc_size(heap, (FAR void *)alignedchunk));
|
||||
alignedchunk = (uintptr_t)kasan_unpoison
|
||||
((FAR const void *)alignedchunk,
|
||||
mm_malloc_size(heap,
|
||||
(FAR void *)alignedchunk));
|
||||
|
||||
DEBUGASSERT(alignedchunk % alignment == 0);
|
||||
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_ADD_BACKTRACE(heap, (FAR char *)newmem - MM_SIZEOF_ALLOCNODE);
|
||||
|
||||
kasan_unpoison(newmem, mm_malloc_size(heap, newmem));
|
||||
if (newmem != oldmem)
|
||||
newmem = kasan_unpoison(newmem, mm_malloc_size(heap, newmem));
|
||||
if (kasan_reset_tag(newmem) != kasan_reset_tag(oldmem))
|
||||
{
|
||||
/* Now we have to move the user contents 'down' in memory. memcpy
|
||||
* 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);
|
||||
#endif
|
||||
kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
||||
ret = kasan_unpoison(ret, mm_malloc_size(heap, ret));
|
||||
|
||||
#ifdef CONFIG_MM_FILL_ALLOCATIONS
|
||||
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);
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user