KASAN ['kæzən]

KASAN 是 Kernel Address Sanitizer
的缩写,它是一个动态检测内存错误的工具,主要功能是检查内存越界访问和使用已释放的内存等问题。KASAN 集成在 Linux 内核中,随 Linux
内核代码一起发布,并由内核社区维护和发展。本文简要介绍 KASAN 的原理及使用方法。

1. 如何打开KASAN功能

Kernel defconfig增加如下配置:

CONFIG_SLUB_DEBUG=y

CONFIG_SLUB_DEBUG_ON=y

CONFIG_KASAN =y

CONFIG_KASAN_INLINE=y

由于1/8的内存用于shadow memory,可用内存会减少1/8,例如8GB的内存,打开KASAN后,MemTotal约为6.72GB。

#ifdef CONFIG_KASAN
#define KASAN_SHADOW_SIZE    (UL(1) << (VA_BITS - 3))
#define KASAN_THREAD_SHIFT    1
#else
#define KASAN_SHADOW_SIZE    (0)
#define KASAN_THREAD_SHIFT    0
#endif

C:Users>adb shell "cat /proc/meminfo | grep MemTotal"
MemTotal:        6723572 kB

2. KASAN原理概述

KASAN利用额外的内存标记可用内存的状态,这部分额外的内存被称作shadow memory(影子区),KASAN将1/8的内存用作shadow
memory。使用特殊的magic num填充shadow memory,在每一次load/store内存的时候检测对应的shadow
memory确定操作是否valid。连续8 bytes内存(8 bytes align)使用1 byte shadow memory标记。

如果8 bytes内存都可以访问,则shadow memory的值为0;如果连续N(1 =< N <= 7) bytes可以访问,则shadow
memory的值为N;如果8 bytes内存访问都是invalid,则shadow memory的值为负数。

例如:

adrp x0, 0xffffffc08821e810

mov w1,#0x5

bl_asan_store1

strb w1,[x0]

 这段汇编指令是往0xffffffc08821e810地址写5,当打开Kasan时,编译器会自动插入红色的bl __asan_store1
指令,__asan_store1函数就是检测一个地址对应的shadow memory的值是否允许写1 byte,蓝色汇编指令是真正的内存访问。

3. 如何根据shadow memory的值判断内存访问操作是否合法?

shadow memory检测原理的实现主要就是__asan_load##size()和__asan_store##size()
函数。KASAN如何根据访问的address以及对应的shadow memory的状态值来判断访问是否合法呢?

__asan_load##size/__asan_store##size

        check_memory_region_inline

                memory_is_poisoned

                       memory_is_poisoned_1

                                return unlikely(last_accessible_byte >=
shadow_value)

                        memory_is_poisoned_2_4_8

                        memory_is_poisoned_16

 

 1)当访问8 bytes时,*shadow_memory=0,访问是valid,否则是invalid;

2)当访问N bytes (N=1,2,4)时,if (*shadow && *shadow > ((unsigned long)addr & 7) +N)
)

访问valid;否则,是invalid;(理解有误,需要对比源码)

4. 伙伴系统分配的内存shadow memory值如何填充

 (1) 从buddy system分配内存

 Step1:假如从buddy system分配4 pages,系统首先从order=2的链表中摘下一块内存;

Step2:然后根据shadow memory address和memory address的对应关系找到对应的shadow memory;对应关系为:

Shadow_addr = (addr >> 3) + KASAN_SHADOW_OFFSET,右移3bit的原因是8 byte memory对应1
byte shadow memory;

Step3:填充shadow memory的内容,分配的4 pages均可访问,填充为0;

(2) 从buddy system释放内存

 Step1:从buddy system order = 2的链表中释放4 pages;

Step2:根据shadow memory addr和memory addr的对应关系,找到shadow memory;

Step3:将shadow memory对应的内存区域2KB(16KB/8)填充为0xFF(KASAN_FREE_PAGE);

5. slub分配的内存shadow memory值如何填充

(1) 从slub cache分配内存

 

Step1:kmalloc(20)会匹配到kmalloc-32的kmem_cache,实际分配的object大小是32 bytes。剩下的12 bytes
KASAN会标记为不可访问状态;

Step2:根据shadow memory addr和memory addr的对应关系找到shadow meory;

Step3:填充shadow memory的内容为00 00 04 FC,具体含义为:

1)前面2个00表示第0~15 byte均可访问
2)04表示第16~23 byte只有前面4 bytes可以访问
3)FC表示第24~31 byte为 KASAN_KMALLOC_REDZONE,不可访问

Step4:保存内存分配的call-stack;

Step5:如果访问了REDZONE区域,KASAN会report out-of-bounds bug;

(2) 从slub cache释放内存

Step1:从slub cache(kmalloc-32) free 20 bytes内存;

Step2:根据memory addr找到shadow memory addr;

Step3:将shadow memory的4 bytes填充为FB(KASAN_KMALLOC_FREE);

Step4:保存slub free的call-stack;

Step5:如果访问了 FB对应的内存,KASAN会报use-after-free bug;

6. 其他形式分配的内存shadow memory如何填充?

全局变量/栈分配的内存填充原理和前面类似,实现有些差异,这里不一一赘述。

7. KASAN report bug举例

 

 

二、总结

KASAN通过建立影子内存来管理内存访问的合法性,可以有效检测内存越界等问题,但无法发现因逻辑问题导致的合法内存的内容改写问题。

一般是针对,DDR损坏,很有效。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信