Chinaunix首页 | 论坛 | 博客
  • 博客访问: 294172
  • 博文数量: 44
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1354
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-08 15:38
个人简介

人生像是在跑马拉松,能够完赛的都是不断地坚持向前迈进;人生就是像在跑马拉松,不断调整步伐,把握好分分秒秒;人生还是像在跑马拉松,能力决定了能跑短程、半程还是全程。人生其实就是一场马拉松,坚持不懈,珍惜时间。

文章分类

分类: LINUX

2016-12-23 14:10:53

前面分析了kmemcheck的实现,那么现在就针对其功能进行试验,鉴于kmemcheck支持KMEMCHECK_SHADOW_UNALLOCATEDKMEMCHECK_SHADOW_UNINITIALIZEDKMEMCHECK_SHADOW_FREED的检测且检测上报信息大同小异,本文中的实验主要针对于未初始化的内存进行。

如果需要使能kmemcheck,需要进行一系列的内核参数设置,具体的配置项可以参考内核源码Document目录下的kmemcheck.txt文档描述(不过文档更新的节奏似乎跟不上源码的更新速度,有部分存在偏差)。至于修改配置项make menuconfig,内核编译的设置选项中设置即可。

主要的配置项有:

CONFIG_CC_OPTIMIZE_FOR_SIZE=n

——该项修改为关闭, 设置路径:"General setup" / "Optimize for size"。如果没有关闭该项,Gcc编译器将会进行优化,这将会导致错误地触发kmemcheck上报非问题。比如代码中访问16bit的数据,Gcc则会先加载32bit的数据,然后丢弃高16bit的信息,而kmemcheck只会看到操作了32bit的数据,并且高16bit可能未初始化,那么将会触发问题上报;

CONFIG_SLAB=y or CONFIG_SLUB=y

——选择了slub算法,设置路径:"General setup" / "Choose SLAB Allocator"

CONFIG_FUNCTION_TRACER=n

——该选项设置为关闭,设置路径:"Kernel hacking" / "Tracers" / "Kernel Function Tracer”。当函数轨迹编译进去的时候,在每个函数调用之前,Gcc都将会向其他函数发出一个调用。这将导致缺页异常处理被调用的时候,ftrace的框架将会在kmemcheck之前调动并获得机会去处理该异常。如果ftrace修改了kmemcheck所依赖的内存信息,将会导致无限的缺页异常循环。

CONFIG_DEBUG_PAGEALLOC= n

——配置项设置为关闭,似乎不太影响。文档路径: "Kernel hacking" / "Debug page memory allocations"。实际设置路径:"Kernel hacking"/”Memory Debugging”/"Debug page memory allocations"

CONFIG_DEBUG_INFO=y

——该配置项设置为开启,设置路径:"Kernel hacking"/”Compile-time checks and compiler options”/”Compile the kernel with debug info”

经过前面的设置,将可以看到kmemcheck调试功能开启选项。具体路径:"Kernel hacking" / "Memory Debugging" / "kmemcheck: trap use of uninitialized memory")。在该设置路径里面有详细的子选项可供设置。

具体子选项有:

CONFIG_KMEMCHECK_[DISABLED | ENABLED | ONESHOT]_BY_DEFAULT

——该设置选项,用于设置Kmemcheck的状态,其中DISABLED表示kmemcheck功能关闭(值为0),而ENABLED表示系统引导之初就使能kmemcheck功能(其值为1),最后的是ONESHOT表示kmemcheck功能仅捕获一次而后自动关闭(其值为2)。如果把握不准的话,可以默认设置功能关闭,因为在系统启动后通过修改/proc/sys/kernel/kmemcheck的值来进行动态调整的,将文件中的修改为对应的状态值即可。该配置项建议修改为DISABLED,后期系统运行后再修改配置文件进行开启,否则系统启动的时候会很慢很慢。

CONFIG_KMEMCHECK_QUEUE_SIZE

——在设置选项中描述为 “kmemcheck: error queue size”

CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT

——在设置选项中描述为 “kmemcheck: shadow copy size (5 => 32 bytes, 6 => 64 bytes)”

CONFIG_KMEMCHECK_PARTIAL_OK

——在设置选项中描述为“kmemcheck: allow partially uninitialized memory”

CONFIG_KMEMCHECK_BITOPS_OK

——在设置选项中描述为“kmemcheck: allow bit-field manipulation”

本文中的示例此几项的配置为:

其实这些配置项信息都无需记住路径在何处,只需要在make menuconfig的时候,使用右上至左下的斜杠 “/”进入搜索界面,直接搜索上面的配置项即可得到详细的路径信息,无需在乎内核版本如何更新。

将上述的配置项进行配置后,重新编译内核以及安装后,即可进行kmemcheck的测试实验了(由于pc的发展,32位环境已经无法接近于绝迹了,只好找了个64位的环境作为演示示例了)。具体的内核模块如何编写,这里就不赘述了,后期有机会再进行补充。示例实验测试代码:

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/mm.h>
  4. #include <asm/page.h>
  5.  
  6. struct page *pages;
  7.  
  8. void kmemchk_access_uninitialized(char *addr)
  9. {
  10.     int offset = 23;
  11.  
  12.     printk("[Kmemchk]: access mem page(%p) offset(%d) \n", addr, offset);
  13.     if (*(addr + offset) == 'a') /*此处尝试访问未被初始化的内存*/
  14.     {
  15.         printk("[Kmemchk]: Fail to hit a ramdon char \n");
  16.     }
  17. }
  18.  
  19. static int __init kmemchk_uninitialized_init(void)
  20. {
  21.     char *addr;
  22.    
  23.     printk("[Kmemchk]: kmemchk_uninitialized_init: \n");
  24.  
  25.     pages = alloc_pages(GFP_KERNEL, 1);
  26.     if (!pages)
  27.     {
  28.         printk("[Kmemchk]: alloc_pages() allocation failed!\n");
  29.     }
  30.     else
  31.     {
  32.         addr = page_address(pages);
  33.  
  34.         kmemchk_access_uninitialized(addr);
  35.     }
  36.  
  37.     return 0;
  38. }
  39.  
  40. static void __exit kmemchk_uninitialized_exit(void)
  41. {
  42.     if (pages)
  43.     {
  44.         __free_pages(pages,1);
  45.     }
  46.  
  47.     printk("[Kmemchk]: kmemchk_uninitialized_exit now \n");
  48. }
  49.  
  50. module_init(kmemchk_uninitialized_init)
  51. module_exit(kmemchk_uninitialized_exit)
  52.  
  53. MODULE_LICENSE("GPL");

Makefile编译脚本:

  1. obj-m = kmemcheck_test.o
  2. all:
  3. make -C /lib/modules/`uname -r`/build M=`pwd`
  4. clean:
  5. rm -f *.o *.ko *.mod.c modules.order Module.symvers

通过Makefile编译出kmemcheck_test.ko后,通过insmod命令即可将该模块加载到环境中,然后通过dmesg即可看到执行结果:

详细的错误信息解析如下:

  1. [ 2499.928090] [Kmemchk]: kmemchk_uninitialized_init:
  2. [ 2499.928094] [Kmemchk]: access mem page(ffff88036a4ac000) offset(23)
  3. [ 2499.929842] WARNING: kmemcheck: Caught 8-bit read from uninitialized memory (ffff88036a4ac017)
  4. #此行记录了告警类型,以及访问的未初始化内存空间地址0xffff88036a4ac017
  5. [ 2499.929843] 80655b6b0388ffff0200000000000000000000000000000000f0ffffff7f0000
  6. #此处是dump出来的内存数据,具体长度取决于CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT配置项
  7. [ 2499.929851] u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u u
  8. [ 2499.929858] ^
  9. #此处的u表示KMEMCHECK_SHADOW_UNINITIALIZED类型的错误,此外KMEMCHECK_SHADOW_UNALLOCATED和
  10. #KMEMCHECK_SHADOW_FREED类型的错误则分别由a和f表示;而^则指出了引致错误的内存访问位置。
  11. [ 2499.929859] RIP: 0010:[] [] kmemchk_access_uninitialized+0x1c/0x33 [kmemcheck_test]
  12. #这里指出了报出告警的指令地址以及函数和其函数内指令偏移。
  13. [ 2499.929862] RSP: 0018:ffff88036a4cfc60 EFLAGS: 00010286
  14. [ 2499.929862] RAX: 0000000000000038 RBX: ffff88036a4ac000 RCX: 0000000000000006
  15. [ 2499.929863] RDX: 0000000000000000 RSI: 0000000000000246 RDI: ffff88037020dc30
  16. [ 2499.929864] RBP: ffff88036a4cfc68 R08: 0000000000000002 R09: 00000000000003c8
  17. [ 2499.929865] R10: 00003ffffffff000 R11: 00000000000003c8 R12: ffff88036a455a80
  18. [ 2499.929865] R13: 0000000000000000 R14: ffffffffc0006000 R15: ffffffffc0784000
  19. [ 2499.929866] FS: 0000000000000000(0000) GS:ffff880370200000(0000) knlGS:0000000000000000
  20. [ 2499.929867] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  21. [ 2499.929868] CR2: ffff880368e561d8 CR3: 0000000002c0a000 CR4: 00000000003406f0
  22. [ 2499.929869] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
  23. [ 2499.929870] DR3: 0000000000000000 DR6: 00000000ffff4ff0 DR7: 0000000000000400
  24. [ 2499.929870] [] 0xffffffffc000606f
  25. [ 2499.929872] [] do_one_initcall+0xae/0x1f0
  26. [ 2499.929874] [] do_init_module+0x55/0x1c1
  27. [ 2499.929876] [] load_module+0x208a/0x26a0
  28. [ 2499.929878] [] SYSC_finit_module+0xb4/0xe0
  29. [ 2499.929880] [] SyS_finit_module+0x9/0x10
  30. [ 2499.929882] [] entry_SYSCALL_64_fastpath+0x16/0x75
  31. [ 2499.929884] [] 0xffffffffffffffff

根据错误信息可以看到此处报出来的告警在kmemchk_access_uninitialized(),位于偏移0x1c的指令访问了未被初始化的内存空间。继而将编译生成的kmemcheck_test.ko进行反汇编,找到kmemchk_access_uninitialized()的该指令。

  1. 0000000000000000 <kmemchk_access_uninitialized>:
  2.    0: 55 push %rbp
  3.    1: 48 89 fe mov %rdi,%rsi
  4.    4: ba 17 00 00 00 mov $0x17,%edx
  5.    9: 48 89 e5 mov %rsp,%rbp
  6.    c: 53 push %rbx
  7.    d: 48 89 fb mov %rdi,%rbx
  8.   10: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
  9.   17: e8 00 00 00 00 callq 1c <kmemchk_access_uninitialized+0x1c>
  10.   1c: 80 7b 17 61 cmpb $0x61,0x17(%rbx)
  11.   20: 74 03 je 25 <kmemchk_access_uninitialized+0x25>
  12.   22: 5b pop %rbx
  13.   23: 5d pop %rbp
  14.   24: c3 retq
  15.   25: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
  16.   2c: e8 00 00 00 00 callq 31 <kmemchk_access_uninitialized+0x31>
  17.   31: eb ef jmp 22 <kmemchk_access_uninitialized+0x22>

cmpb   $0x61,0x17(%rbx)指令,可以分析得出该比较指令对应的即为函数中的if (*(addr + offset) == 'a')该条件判断操作。

由此一来便可以定位此类对未初始化内存进行访问的不合法的信息。

阅读(3675) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~