Kernel address sanitizer (Kasan) 是一款随 Linux 内核代码一同发布和维护的内存检测工具,由内核社区维护和发展。本文简要介绍 Kasan 的原理及使用方法。
引言Kasan 是 Kernel Address Sanitizer 的缩写,它是一个动态检测内存错误的工具,主要功能是检查内存越界访问和使用已释放的内存等问题。Kasan 集成在 Linux 内核中,随 Linux 内核代码一起发布,并由内核社区维护和发展。
背景Kasan 可以追溯到 LLVM 的 sanitizers 项目(https://github.com/google/sanitizers),这个项目包含了 AddressSanitizer,MemorySanitizer,ThreadSanitizer 和 LeakSanitizer 等工具。但这些工具只能检测用户空间的内存问题。通过在编译时加入指定的选项,就可以给用户程序加入 Address Sanitizer 功能。
清单 1. 用户空间内存错误代码实例// To compile: g++ -O -g -fsanitize=address use-after-free.c int main(int argc, char **argv) { int *array = new int[10]; delete [] array; return array[argc]; // BOOM }
当运行以上有内存使用错误的程序时,加入 Address Sanitizer 功能的的版本会报告如下的错误信息,而没有任何选项的版本则会正常结束程序。
清单 2. Address Sanitizer 运行结果tengrui@virtualbox:~/workspace/cc$ g++ -O -g -fsanitize=address use-after-free.cc tengrui@virtualbox:~/workspace/cc$ ./a.out ================================================================= ==4206==ERROR: AddressSanitizer: heap-use-after-free on address 0x60400000dfd4 at pc 0x0000004007d4 bp 0x7ffdfdd414f0 sp 0x7ffdfdd414e0 READ of size 4 at 0x60400000dfd4 thread T0 #0 0x4007d3 in main /home/tengrui/workspace/cc/use-after-free.cc:4 #1 0x7f8aa150882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #2 0x4006b8 in _start (/home/tengrui/workspace/cc/a.out+0x4006b8) 0x60400000dfd4 is located 4 bytes inside of 40-byte region [0x60400000dfd0,0x60400000dff8) freed by thread T0 here: #0 0x7f8aa194abca in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99bca) #1 0x4007a7 in main /home/tengrui/workspace/cc/use-after-free.cc:3 previously allocated by thread T0 here: #0 0x7f8aa194a5d2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x995d2) #1 0x400797 in main /home/tengrui/workspace/cc/use-after-free.cc:2 SUMMARY: AddressSanitizer: heap-use-after-free /home/tengrui/workspace/cc/use-after-free.cc:4 main Shadow bytes around the buggy address: 0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd fd fa 0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe ==4206==ABORTING
Andrey Ryabinin 借鉴了 AddressSanitizer 的思想,并在 Linux 内核中实现了 Kernel Address Sanitizer。所以 Kasan 也可以看成是用于内核空间的 Address Sanitizer。
原理Kasan 的原理是利用“额外”的内存来标记那些可以被使用的内存的状态。这些做标记的区域被称为影子区域(shadow region)。了解 Linux 内存管理的读者知道,内存中的每个物理页在内存中都会有一个 struct page 这样的结构体来表示,即每 4KB 的页需要 40B 的结构体,大约 1% 的内存用来表示内存本身。Kasan 与其类似但“浪费”更为严重,影子区域的比例是 1:8,即总内存的九分之一会被“浪费”。用官方文档中的例子,如果有 128TB 的可用内存,需要有额外 16TB 的内存用来做标记。
做标记的方法比较简单,将可用内存按照 8 子节的大小分组,如果每组中所有 8 个字节都可以访问,则影子内存中相应的地方用全零(0x00)表示;如果可用内存的前 N(1 到 7 范围之间)个字节可用,则影子内存中响应的位置用 N 表示;其它情况影子内存用负数表示该内存不可用。
图 1. Kasan 内存布局原理 使用