目录
KASan(kernel address sanitizer)是一个内核态的动态内存错误检测器,提供了强大的内存错误(out-of-bound and use-after-free)检测功能:
-
缓冲区溢出
①、堆内存溢出
②、栈上内存溢出
③、全局区缓存溢出 -
悬空指针
①、使用释放后的堆上内存
②、使用返回的栈上内存
③、使用退出作用域的变量 -
...
由于KASan在内核版本V4.0后才加入,而且内核编译时默认是不启用KASan的,所以需要重新编译配置内核才能启用该功能(gcc版本要求5.0及以上才完全支持)。
本文实验环境:
root@ubuntu:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.6 LTS
Release: 16.04
Codename: xenial
root@ubuntu:~#
root@ubuntu:~# uname -r
4.4.0-142-generic
root@ubuntu:~#
内容提要:
①先将ubuntu16.04的4.4.0内核编译升级到linux-4.4.252版本;
②编写一个简单的内核模块看下kasan的强大检测功能。
一、编译内核并配置支持KAsan
1、下载源码
在Linux官网下载内核源码,我这里选择了linux-4.4.252源码。
下载完成后,解压到/usr/src下。
root@ubuntu:~# cd /usr/src/
root@ubuntu:/usr/src# wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.4.252.tar.gz
root@ubuntu:/usr/src# tar xf linux-4.4.252.tar.gz
root@ubuntu:/usr/src# cd linux-4.4.252
2、安装编译内核必须的库
apt-get install libssl-dev build-essential openssl -y
apt-get install zlibc minizip libidn11-dev libidn11 -y
apt-get install libncurses5-dev
3、清理之前内核编译生成及相关配置
这里是为下面全新编译内核做准备:
root@ubuntu:/usr/src/linux-4.4.252# make mrproper
CLEAN scripts/basic
CLEAN scripts/kconfig
CLEAN include/config include/generated
root@ubuntu:/usr/src/linux-4.4.252# make clean
root@ubuntu:/usr/src/linux-4.4.252#
4、配置内核参数
root@ubuntu:/usr/src/linux-4.4.252# make menuconfig
依次选择Kernel hacking > Memory Debugging,然后配置内存debug选项,这里我把大部分debug选项都选中了:
其中,KASan选项中Instrumentation type选inline的:
配置完成后,exit退出,最后选择yes保存配置项。
5、开始编译
make -j4
内核编译比较耗时,一般都要半个小时到几个小时(取决于机器性能),为了加快内核编译速度,这里我启用4个线程编译内核,你可以根据自己机器的cpu核数进行调整(lscpu命令可以查看cpu核数)。
6、模块(驱动)安装
root@ubuntu:/usr/src/linux-4.4.252# make modules_install
7、安装新编译的内核
root@ubuntu:/usr/src/linux-4.4.252# make install
sh ./arch/x86/boot/install.sh 4.4.252 arch/x86/boot/bzImage \
System.map "/boot"
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.4.252 /boot/vmlinuz-4.4.252
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.4.252 /boot/vmlinuz-4.4.252
update-initramfs: Generating /boot/initrd.img-4.4.252
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 4.4.252 /boot/vmlinuz-4.4.252
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Found linux image: /boot/vmlinuz-4.4.252
Found initrd image: /boot/initrd.img-4.4.252
Found linux image: /boot/vmlinuz-4.4.0-171-generic
Found initrd image: /boot/initrd.img-4.4.0-171-generic
Found linux image: /boot/vmlinuz-4.4.0-170-generic
Found initrd image: /boot/initrd.img-4.4.0-170-generic
Found linux image: /boot/vmlinuz-4.4.0-142-generic
Found initrd image: /boot/initrd.img-4.4.0-142-generic
done
8、修改内核启动顺序
root@ubuntu:~# grep menuentry /boot/grub/grub.cfg
if [ x"${feature_menuentry_id}" = xy ]; then
menuentry_id_option="--id"
menuentry_id_option=""
export menuentry_id_option
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.252' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.252-advanced-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.252 (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.252-recovery-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-171-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-171-generic-advanced-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-171-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-171-generic-recovery-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-170-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-170-generic-advanced-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-170-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-170-generic-recovery-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-142-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-142-generic-advanced-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
menuentry 'Ubuntu, with Linux 4.4.0-142-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.4.0-142-generic-recovery-39f6e796-6e9e-41ae-89db-fade0f403ad4' {
修改配置:
root@ubuntu:~# vi /etc/default/grub
修改/etc/default/grub文件里GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 4.4.252",红色部分即为上面查找到的新生成的menuentry值。
然后执行update-grub命令,使改动生效:
root@ubuntu:~# update-grub
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Found linux image: /boot/vmlinuz-4.4.252
Found initrd image: /boot/initrd.img-4.4.252
Found linux image: /boot/vmlinuz-4.4.0-171-generic
Found initrd image: /boot/initrd.img-4.4.0-171-generic
Found linux image: /boot/vmlinuz-4.4.0-170-generic
Found initrd image: /boot/initrd.img-4.4.0-170-generic
Found linux image: /boot/vmlinuz-4.4.0-142-generic
Found initrd image: /boot/initrd.img-4.4.0-142-generic
done
root@ubuntu:~#
9、reboot
重启之后,可以查看验证:
root@ubuntu:~# uname -r
4.4.252
root@ubuntu:~#
root@ubuntu:~# grep -nr KASAN /boot/config-4.4.252
38:CONFIG_KASAN_SHADOW_OFFSET=0xdffffc0000000000
7901:CONFIG_HAVE_ARCH_KASAN=y
7902:CONFIG_KASAN=y
7903:# CONFIG_KASAN_OUTLINE is not set
7904:CONFIG_KASAN_INLINE=y
7905:CONFIG_TEST_KASAN=m
可以看到,KASan相关的配置都已经启用了。
二、编写测试内核模块
1、测试代码编写
命名为my_lkm.c文件:
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>
#include <linux/kernel.h> // __FUNCTION__
#include <linux/slab.h> // kmalloc kfree
MODULE_LICENSE("GPL");
static int __init test_init(void)
{
char *ptr;
size_t size = 124;
printk("out-of-bounds to right\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
printk(KERN_ERR "Allocation failed\n");
return -1;
}
printk("ptr address: %p\n", ptr);
ptr[size] = 'x';
printk("ptr[size] address: %p\n", ptr + size);
kfree(ptr);
return 0;
}
static void __exit test_exit(void)
{
printk("%s removed.\n",__func__);
}
module_init(test_init);
module_exit(test_exit);
上述代码中,我们从slab中申请了一个指向124个字节大小的内存空间指针,然后用数组形式访问其第125个字节空间内容,这里故意越界访问,这样kasan会报slab-out-of-bounds错误,见下面系统日志输出。
2、编写Makefile文件
vi Makefile
内容如下:
obj-m += my_test_lkm.o
my_test_lkm-objs := my_lkm.o
EXTRA_CFLAGS += -g -Wall
all:
make -C /usr/src/linux-4.4.252/ M=$(PWD) modules
clean:
make -C /usr/src/linux-4.4.252/ M=$(PWD) clean
3、编译内核模块
root@ubuntu:/home/test_ko/lkm-test05# make
make -C /usr/src/linux-4.4.252/ M=/home/test_ko/lkm-test05 modules
make[1]: Entering directory '/usr/src/linux-4.4.252'
CC [M] /home/test_ko/lkm-test05/my_lkm.o
LD [M] /home/test_ko/lkm-test05/my_test_lkm.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/test_ko/lkm-test05/my_test_lkm.mod.o
LD [M] /home/test_ko/lkm-test05/my_test_lkm.ko
make[1]: Leaving directory '/usr/src/linux-4.4.252'
root@ubuntu:/home/test_ko/lkm-test05#
4、insmod插入内核模块
insmod my_test_lkm.ko
5、dmesg -Tw查看系统内核日志
可以看到系统内核日志中,KASan报BUG: KASAN: slab-out-of-bounds in test_init+0xa2/0x1000 ,并且下面详细列出了在哪里申请的(Allocated)、哪里释放的(Freed)及整个调用链的详细信息:
[Tue Jan 19 18:27:29 2021] out-of-bounds to right
[Tue Jan 19 18:27:29 2021] ptr address: ffff8800badbd910
[Tue Jan 19 18:27:29 2021] ==================================================================
[Tue Jan 19 18:27:29 2021] BUG: KASAN: slab-out-of-bounds in test_init+0xa2/0x1000 [my_test_lkm] at addr ffff8800badbd98c
[Tue Jan 19 18:27:29 2021] Write of size 1 by task insmod/100870
[Tue Jan 19 18:27:29 2021] =============================================================================
[Tue Jan 19 18:27:29 2021] BUG kmalloc-128 (Tainted: G B OE ): kasan: bad access detected
[Tue Jan 19 18:27:29 2021] -----------------------------------------------------------------------------
[Tue Jan 19 18:27:29 2021] INFO: Allocated in test_init+0x4c/0x1000 [my_test_lkm] age=4 cpu=0 pid=100870
[Tue Jan 19 18:27:29 2021] ___slab_alloc+0x4d9/0x550
[Tue Jan 19 18:27:29 2021] __slab_alloc+0x20/0x40
[Tue Jan 19 18:27:29 2021] kmem_cache_alloc_trace+0x24c/0x2e0
[Tue Jan 19 18:27:29 2021] test_init+0x4c/0x1000 [my_test_lkm]
[Tue Jan 19 18:27:29 2021] do_one_initcall+0x143/0x300
[Tue Jan 19 18:27:29 2021] do_init_module+0x1d9/0x4de
[Tue Jan 19 18:27:29 2021] load_module+0x6a4f/0xa160
[Tue Jan 19 18:27:29 2021] SYSC_finit_module+0x126/0x160
[Tue Jan 19 18:27:29 2021] SyS_finit_module+0xe/0x10
[Tue Jan 19 18:27:29 2021] entry_SYSCALL_64_fastpath+0x22/0x9e
[Tue Jan 19 18:27:29 2021] INFO: Freed in load_elf_binary+0x220/0x4480 age=14729 cpu=0 pid=100293
[Tue Jan 19 18:27:29 2021] __slab_free+0x1bc/0x300
[Tue Jan 19 18:27:29 2021] kfree+0x106/0x1e0
[Tue Jan 19 18:27:29 2021] load_elf_binary+0x220/0x4480
[Tue Jan 19 18:27:29 2021] search_binary_handler+0x156/0x430
[Tue Jan 19 18:27:29 2021] do_execveat_common.isra.31+0x1025/0x1ae0
[Tue Jan 19 18:27:29 2021] SyS_execve+0x3a/0x50
[Tue Jan 19 18:27:29 2021] return_from_execve+0x0/0x23
[Tue Jan 19 18:27:29 2021] INFO: Slab 0xffffea0002eb6e00 objects=71 used=30 fp=0xffff8800badbb738 flags=0x1ffff8000004080
[Tue Jan 19 18:27:29 2021] INFO: Object 0xffff8800badbd910 @offset=22800 fp=0xffff8800badb9728
[Tue Jan 19 18:27:29 2021] Bytes b4 ffff8800badbd900: 19 37 d1 00 01 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a .7......ZZZZZZZZ
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd910: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd920: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd930: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd940: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd950: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd960: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd970: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
[Tue Jan 19 18:27:29 2021] Object ffff8800badbd980: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk.
[Tue Jan 19 18:27:29 2021] Redzone ffff8800badbd990: cc cc cc cc cc cc cc cc ........
[Tue Jan 19 18:27:29 2021] Padding ffff8800badbdad0: 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZ
[Tue Jan 19 18:27:29 2021] CPU: 0 PID: 100870 Comm: insmod Tainted: G B OE 4.4.252 #1
[Tue Jan 19 18:27:29 2021] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/29/2019
[Tue Jan 19 18:27:29 2021] 0000000000000000 41692362580668d4 ffff8800a1287898 ffffffff82988c2f
[Tue Jan 19 18:27:29 2021] ffff88011840f580 ffff8800badbd910 ffff8800a12878c8 ffffffff81585cc9
[Tue Jan 19 18:27:29 2021] ffff88011840f580 ffffea0002eb6e00 ffff8800badbd910 0000000000000001
[Tue Jan 19 18:27:29 2021] Call Trace:
[Tue Jan 19 18:27:29 2021] [<ffffffff82988c2f>] dump_stack+0x6d/0x8b
[Tue Jan 19 18:27:29 2021] [<ffffffff81585cc9>] print_trailer+0xf9/0x150
[Tue Jan 19 18:27:29 2021] [<ffffffff8158cda4>] object_err+0x34/0x40
[Tue Jan 19 18:27:29 2021] [<ffffffff8158f5ad>] kasan_report.part.2+0x21d/0x520
[Tue Jan 19 18:27:29 2021] [<ffffffffc0a500a2>] ? test_init+0xa2/0x1000 [my_test_lkm]
[Tue Jan 19 18:27:29 2021] [<ffffffff81589fb0>] ? kmem_cache_alloc_trace+0x120/0x2e0
[Tue Jan 19 18:27:29 2021] [<ffffffffc0a50000>] ? 0xffffffffc0a50000
[Tue Jan 19 18:27:29 2021] [<ffffffff8158f9d1>] __asan_report_store1_noabort+0x31/0x40
[Tue Jan 19 18:27:29 2021] [<ffffffffc0a500a2>] test_init+0xa2/0x1000 [my_test_lkm]
[Tue Jan 19 18:27:29 2021] [<ffffffff810021b3>] do_one_initcall+0x143/0x300
[Tue Jan 19 18:27:29 2021] [<ffffffff81002070>] ? try_to_run_init_process+0x40/0x40
[Tue Jan 19 18:27:29 2021] [<ffffffff8158eca6>] ? kasan_unpoison_shadow+0x36/0x50
[Tue Jan 19 18:27:29 2021] [<ffffffff8158eca6>] ? kasan_unpoison_shadow+0x36/0x50
[Tue Jan 19 18:27:29 2021] [<ffffffff8158ed1e>] ? kasan_kmalloc+0x5e/0x70
[Tue Jan 19 18:27:29 2021] [<ffffffff8158eca6>] ? kasan_unpoison_shadow+0x36/0x50
[Tue Jan 19 18:27:29 2021] [<ffffffff8158edb7>] ? __asan_register_globals+0x87/0xa0
[Tue Jan 19 18:27:29 2021] [<ffffffff82985bf4>] do_init_module+0x1d9/0x4de
[Tue Jan 19 18:27:29 2021] [<ffffffff812fc3df>] load_module+0x6a4f/0xa160
[Tue Jan 19 18:27:29 2021] [<ffffffff812f0c00>] ? m_show+0x4b0/0x4b0
[Tue Jan 19 18:27:29 2021] [<ffffffff812f5990>] ? module_frob_arch_sections+0x20/0x20
[Tue Jan 19 18:27:29 2021] [<ffffffff815eeedb>] ? kernel_read+0xeb/0x1a0
[Tue Jan 19 18:27:29 2021] [<ffffffff815eedf0>] ? open_exec+0x50/0x50
[Tue Jan 19 18:27:29 2021] [<ffffffff812f10dd>] ? copy_module_from_fd.isra.50+0x1dd/0x2f0
[Tue Jan 19 18:27:29 2021] [<ffffffff812ffe26>] SYSC_finit_module+0x126/0x160
[Tue Jan 19 18:27:29 2021] [<ffffffff812ffd00>] ? SYSC_init_module+0x210/0x210
[Tue Jan 19 18:27:29 2021] [<ffffffff812ffe7e>] SyS_finit_module+0xe/0x10
[Tue Jan 19 18:27:29 2021] [<ffffffff829ab5e5>] entry_SYSCALL_64_fastpath+0x22/0x9e
[Tue Jan 19 18:27:29 2021] Memory state around the buggy address:
[Tue Jan 19 18:27:29 2021] ffff8800badbd880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[Tue Jan 19 18:27:29 2021] ffff8800badbd900: fc fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[Tue Jan 19 18:27:29 2021] >ffff8800badbd980: 00 04 fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[Tue Jan 19 18:27:29 2021] ^
[Tue Jan 19 18:27:29 2021] ffff8800badbda00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[Tue Jan 19 18:27:29 2021] ffff8800badbda80: fc fc fc fc fc fc fc fc fc fc fc 00 00 00 00 00
[Tue Jan 19 18:27:29 2021] ==================================================================
[Tue Jan 19 18:27:29 2021] ptr[size] address: ffff8800badbd98c
本文已同步至我的微信公众号这篇文章:使用KASan动态检测内核态内存错误(基于Ubuntu 16.04)。
欢迎关注我的微信公众号大胖聊编程,也可以加好友一起交流学习。
参考链接:
https://www.kernel.org/doc/html/latest/dev-tools/kasan.html