Linux 内核 KASAN 内存检查工具
开启了 kasan 功能以后,每次分配内存,会有额外的 shadow memory,大小为 1/8,用来标记内存分配的信息。每连续 8 个字节,如果都可以访问,shadow byte 标记为 0;如果 1、2、……、7 个字节可以访问,shadow byte 标记为 1、2、……、7;否则标记为一些预定义的标记值,这些值都小于 0,且代表了具体的内存分配状态:
#define KASAN_FREE_PAGE 0xFF /* page was freed */
#define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */
#define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */
#define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */
#define KASAN_KMALLOC_FREETRACK 0xFA /* object was freed and has free track set */
在每次访存的代码里,编译器会插入特别指令 __asan_loadXX
或者 __asan_storeXX
,这些指令在经过一些封装后,会调用到 check_region_inline()
函数:
static __always_inline bool check_region_inline(unsigned long addr,
size_t size, bool write,
unsigned long ret_ip)
{
if (!kasan_arch_is_ready())
return true;
if (unlikely(size == 0))
return true;
if (unlikely(addr + size < addr))
return !kasan_report(addr, size, write, ret_ip);
if (unlikely((void *)addr <
kasan_shadow_to_mem((void *)KASAN_SHADOW_START))) {
return !kasan_report(addr, size, write, ret_ip);
}
if (likely(!memory_is_poisoned(addr, size)))
return true;
return !kasan_report(addr, size, write, ret_ip);
}
这个函数里,会依次进行检查:
- 检查 KASAN 是否启用;
- 检查访存大小是否为 0,这是不合理的;
- 检查访存区域的结尾处是否在起始处前面,这也是不合理的;
- 检查访存区域与 shadow byte 里的标记是否有冲突,比如 "out-of-bounds"、"use-after-free",等等;
- 如果有不合理的访存发生,会通过 kasan_report() 输出访存错误的类型、当时的调用栈、以及这片内存分配时的调用栈等等信息;
实现代码在 mm/kasan/generic.c
以及其他一些文件里。
参考资料
- mm/kasan/generic.c
- Documentation/dev-tools/kasan.rst
- Documentation/translations/zh_CN/dev-tools/kasan.rst
- https://www.cnblogs.com/linhaostudy/p/14028917.html