linux内存管理实验,Linux-3.14.12内存管理笔记【内存破坏检测kmemcheck示例】

前面分析了kmemcheck的实现,那么现在就针对其功能进行试验,鉴于kmemcheck支持KMEMCHECK_SHADOW_UNALLOCATED、KMEMCHECK_SHADOW_UNINITIALIZED及KMEMCHECK_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”。

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

6d48a0a7b677866f522b74e1a4733a85.png

其实这些配置项信息都无需记住路径在何处,只需要在make

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

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

#include

#include

#include

struct page *pages;

void kmemchk_access_uninitialized(char *addr)

{

int offset = 23;

printk("[Kmemchk]: access mem page(%p) offset(%d) \n", addr, offset);

if (*(addr + offset) == 'a') /*此处尝试访问未被初始化的内存*/

{

printk("[Kmemchk]: Fail to hit a ramdon char \n");

}

}

static int __init kmemchk_uninitialized_init(void)

{

char *addr;

printk("[Kmemchk]: kmemchk_uninitialized_init: \n");

pages = alloc_pages(GFP_KERNEL, 1);

if (!pages)

{

printk("[Kmemchk]: alloc_pages() allocation failed!\n");

}

else

{

addr = page_address(pages);

kmemchk_access_uninitialized(addr);

}

return 0;

}

static void __exit kmemchk_uninitialized_exit(void)

{

if (pages)

{

__free_pages(pages,1);

}

printk("[Kmemchk]: kmemchk_uninitialized_exit now \n");

}

module_init(kmemchk_uninitialized_init)

module_exit(kmemchk_uninitialized_exit)

MODULE_LICENSE("GPL");

Makefile编译脚本:obj-m = kmemcheck_test.o

all:

make -C /lib/modules/`uname -r`/build M=`pwd`

clean:

rm -f *.o *.ko *.mod.c modules.order Module.symvers

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

5111cc12a618bc98c24b928b3ae1f79a.png

详细的错误信息解析如下:[ 2499.928090] [Kmemchk]: kmemchk_uninitialized_init:

[ 2499.928094] [Kmemchk]: access mem page(ffff88036a4ac000) offset(23)

[ 2499.929842] WARNING: kmemcheck: Caught 8-bit read from uninitialized memory (ffff88036a4ac017)

#此行记录了告警类型,以及访问的未初始化内存空间地址0xffff88036a4ac017

[ 2499.929843] 80655b6b0388ffff0200000000000000000000000000000000f0ffffff7f0000

#此处是dump出来的内存数据,具体长度取决于CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT配置项

[ 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

[ 2499.929858] ^

#此处的u表示KMEMCHECK_SHADOW_UNINITIALIZED类型的错误,此外KMEMCHECK_SHADOW_UNALLOCATED和

#KMEMCHECK_SHADOW_FREED类型的错误则分别由a和f表示;而^则指出了引致错误的内存访问位置。

[ 2499.929859] RIP: 0010:[] [] kmemchk_access_uninitialized+0x1c/0x33 [kmemcheck_test]

#这里指出了报出告警的指令地址以及函数和其函数内指令偏移。

[ 2499.929862] RSP: 0018:ffff88036a4cfc60 EFLAGS: 00010286

[ 2499.929862] RAX: 0000000000000038 RBX: ffff88036a4ac000 RCX: 0000000000000006

[ 2499.929863] RDX: 0000000000000000 RSI: 0000000000000246 RDI: ffff88037020dc30

[ 2499.929864] RBP: ffff88036a4cfc68 R08: 0000000000000002 R09: 00000000000003c8

[ 2499.929865] R10: 00003ffffffff000 R11: 00000000000003c8 R12: ffff88036a455a80

[ 2499.929865] R13: 0000000000000000 R14: ffffffffc0006000 R15: ffffffffc0784000

[ 2499.929866] FS: 0000000000000000(0000) GS:ffff880370200000(0000) knlGS:0000000000000000

[ 2499.929867] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033

[ 2499.929868] CR2: ffff880368e561d8 CR3: 0000000002c0a000 CR4: 00000000003406f0

[ 2499.929869] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000

[ 2499.929870] DR3: 0000000000000000 DR6: 00000000ffff4ff0 DR7: 0000000000000400

[ 2499.929870] [] 0xffffffffc000606f

[ 2499.929872] [] do_one_initcall+0xae/0x1f0

[ 2499.929874] [] do_init_module+0x55/0x1c1

[ 2499.929876] [] load_module+0x208a/0x26a0

[ 2499.929878] [] SYSC_finit_module+0xb4/0xe0

[ 2499.929880] [] SyS_finit_module+0x9/0x10

[ 2499.929882] [] entry_SYSCALL_64_fastpath+0x16/0x75

[ 2499.929884] [] 0xffffffffffffffff

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

0: 55 push %rbp

1: 48 89 fe mov %rdi,%rsi

4: ba 17 00 00 00 mov $0x17,%edx

9: 48 89 e5 mov %rsp,%rbp

c: 53 push %rbx

d: 48 89 fb mov %rdi,%rbx

10: 48 c7 c7 00 00 00 00 mov $0x0,%rdi

17: e8 00 00 00 00 callq 1c

1c: 80 7b 17 61 cmpb $0x61,0x17(%rbx)

20: 74 03 je 25

22: 5b pop %rbx

23: 5d pop %rbp

24: c3 retq

25: 48 c7 c7 00 00 00 00 mov $0x0,%rdi

2c: e8 00 00 00 00 callq 31

31: eb ef jmp 22

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值