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:
wangmingrong
2024-03-14 16:40:52 +08:00
committed by Xiang Xiao
parent 9c6bed4b00
commit ae3facda53
17 changed files with 394 additions and 51 deletions
+20 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+199
View File
@@ -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 &region->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;
}
+4 -4
View File
@@ -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);
}
+2
View File
@@ -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;
+1 -1
View File
@@ -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
+4 -2
View File
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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