x86体系结构下Linux kmemcheck浅析

   

因编程习惯,或内核运行期间造成未初始化内存的不经意使用,非常可能导致一些难以检查的错误。如果足够幸运,内核崩溃将直接导致错误报告系统推出一串核态调用栈的追踪序列;否则系统将有可能处于一种无响应的状态。半年前,Vegard Nossumt向Linux开源社区提交了一个基于2.6.25版内核的,旨在监控不当使用未初始化内存的补丁--kmemcheck。

本文主要调研了基于x86体系结构的kmemcheck实现策略,给出了模块中主要代码的逻辑调用关系,以及用于功能验证的内核测试模块(详见附录);扼要的评估了kmemcheck工作性能,和基于非x86体系结构的kmemcheck实现可能性。

  

摘    要    1

第一章  绪 论    2

1.1 kmemcheck系统理论支点及简要说明.. 2

1.2 体系结构的支持情况.. 2

1.3 内核种类的支持程度。.. 2

第二章  过程论述    2

2.1 阅读已有文档和及审核邮件,了解代码基本框架.. 2

2.2主要过程的逻辑调用关系分析。.. 3

2.2.1错误报告系统.. 3

2.2.2读写指令解码.. 3

2.2.3影子内存的管理.. 3

2.2.4缺页异常和单步hook. 4

2.2.5内存管理hook(同时支持 slab和slub).. 5

2.2.6 kmemcheck文档,内核选项配置。.. 6

2.3 kmemcheck实现框架解析.. 7

2.4验证过程.. 8

第三章 总结分析    9

3.1 总结:.. 9

3.1.1 设计原理评价.. 9

3.1.2 x86体系下kmemcheck实现关键点.. 9

3.1.3 系统稳定性评价.. 9

3.1.4 不稳定原因初探.. 9

3.1.5 可能的发展方向.. 9

3.2 结论分析.. 10

3.2.1其他架构kmemcheck的可行性分析.. 10

3.2.2思维突破.. 10

附录:    10

附录一、Kmemcheck的简单测试用例:.. 10

附录二、Kmemcheck测试用例实验结果:.. 12

 

第一章  绪 论

1.1 kmemcheck系统理论支点及简要说明

理论支点:内存中,除了DMA或者其他Notifications之类的数据结构之外,一般的数据块在使用之前都会被系统初始化为某些定值,这些值一般都是系统运行态的初始条件,如果它们在系统运行期间是不确定的,也就是系统的参考系是不确定的,那么该系统的稳定性是值得怀疑的。

简要说明:kmemcheck借助缺页异常和单步调试机制来分析核态空间某个内存地址的读写顺序(如kmalloc分配的地址),旨在监控核态代码对未初始化内存不经意使用的行为轨迹。

1.2 体系结构的支持情况

第一个支持的体系结构是x86;支持arm的kmemcheck 草稿版的补丁 [11]也已经被推到一个的开源社区中,然而值得怀疑的是该体系结构是否有kmemcheck需要的细粒度页表机制,而且该补丁没有贴出相关的测试用例和测试数据,到目前为止也没有的ACK 邮件。另外支持其他体系结构的kmemcheck补丁也未在社区中推出,估计今后也不会。

1.3 内核种类的支持程度。

尽管Kmemcheck的作者曾经在LWN中贴出了一个基于SMP的kmemcheck补丁 [8],不过看起来那个补丁工作的不是很好,而且作者已经把那个补丁从kmemcheck.git库中去掉了,所以kmemcheck目前仅支持单CPU的内核模式。看看Ingo Molnar对kmemcheck中肯恰当的评论:

"kmemcheck不仅消耗了一半的内存去跟踪另一半内存,而且非常慢,因为大多数核态空间的指令都会产生一个缺页、单步中断,这些当然是十足的疯狂,但是kmemcheck的工作是合理的,而且其监控成果是非常有趣、并有很强的吸引力"。一个标准的先抑后扬的说法。顺便提及Ingo Molnar曾经用kmemcheck发现了四个内核错误。

第二章  过程论述

2.1 阅读已有文档和及审核邮件,了解代码基本框架

无论文档多么简单,总是了解一件新事物最快捷的途径,因此不妨依照补丁审核过程,首先仔细阅读补丁中附带的文档,以期达到初步了解kmemcheck之目的。

同时浏览了kmemcheck的所有审核(review)邮件,其目的有四:

第一,从开发者递交的审核邮件中,可以知道kmemcheck的背景,是属于Linux中的那个git分支、那些模块,以及从哪里可得到kmemcheck更多资料(如原代码等);

第二,可以获得对kmemcheck的多视角评价,了解kmemcheck的基本特征;

第三,从审核者和开发者之间的每一回合讨论中,可以快速获得kmemcheck的关键点,及可能潜在的问题。

第四,可以更多地了解不同文化背景工程师的工作模式和语言特点;

 

浏览源代码,了解kmemcheck的基本框架及它与Linux系统的结合点。通过这个环节,基本上可以了解kmemcheck的如下内容:

  • 1) 主要涉及了内存子模块;
  • 2) x86的体系结构和及架构相关的中断行为;
  • 3) 仅支持单核(内核类型)。

了解深度与研究主体对硬件体系结构和Linux内核理解程度密切相关。所以,这个环节非常重要,它可以使我们非常迅速而清楚地知道:需要复习那些硬件内容,尤其是在CPU体系结构飞速发展的今天。

2.2主要过程的逻辑调用关系分析。

结合实验阅读kmemcheck的源代码,经分析可知,kmemcheck通过捕捉核态空间的每次读写操作指令,来监控内存读写操作的顺序,发现并报告错误使用未初始化内存的上下文。具体解释如下:

因为一般情况下,初始化操作总是有一些写指令完成,如果对于某个核态空间地址,如果第一次是读取操作,那么kmemcheck就认为当前核态进程的读指令行为是不当的,它就会把该进程的上下文和调用栈推到标准输出。下面分六部分介绍kmemcheck补丁中的代码结构(每一部分区别包含:核心数据结构、核心内容、代码位置、注意点):

2.2.1错误报告系统

核心数据结构:kmemcheck_tasklet(tasklet_struct 结构)

核心代码位置:arch/x86/mm/kmemcheck/error.c

kmemcheck系统发现异常后,首先保存异常的上下文和调用栈,然后调用tasklet_hi_schedule_first 把 kmemcheck_tasklet加入当前cpu的tasklet_struct的调度序列,在适当的时候tasklet_struct调度器唤醒回调函数do_wakeup向标准输出打印异常报告。

注意:kmemcheck_error_save_bug用来报告kmemcheck异常本身造成的bug,不是内存的不合理使用引起。

2.2.2读写指令解码

核心代码位置arch/x86/mm/kmemcheck/opcode.c

核心函数:(kmemcheck_opcode_decode)

一个非常简单而粗略的x86读写指令解码器,主要是从其他指令中区别出读写指令及其大小。返回实际指令长度,前缀操作码。注意:目前opcode虽然考虑到了重复指令,但是看起来还是没有处理好REP MOVS/STOS,这一点也可以从作者的代码注释中得到旁证。

2.2.3影子内存的管理

核心数据结构:页表结构

核心代码位置:arch/x86/mm/kmemcheck/shadow.c

Kmemcheck大胆的使用了一个非常的策略来标记内存的初始化状态,就是用一块大小与所申请内存(记为B)相同的shadow内存(记作A)来标识B的状态为如下四种之一:(定义在shadow.c)

KMEMCHECK_SHADOW_UNALLOCATED;//未分配的

                  KMEMCHECK_SHADOW_UNINITIALIZED;//未初始化的

                  KMEMCHECK_SHADOW_INITIALIZED;//初始化的

                  KMEMCHECK_SHADOW_FREED;//释放的

这一点就是Ingo Molnar说的,用了将近一半的内存开销来监控另一半内存的状态,从而监控未初始化内存的不当使用。不过在产品的测试阶段,这也无妨。

2.2.4缺页异常和单步hook

核心代码位置arch/x86/mm/kmemcheck/kmemcheck.c

与其他架构比较而言,x86体系结构核心进入缺页异常do_page_fault之前的过程相对比较简单了(硬件做了许多事情),这里暂且不表。

缺页异常:

do_page_fault   (fault.c)

|--kmemcheck_active(检查上次缺页中断是否kmemcheck产生)

|        '-- kmemcheck_hide(regs); Yes(接着完成)

  '--进入核态空间缺页中断处理

        |-- vmalloc_fault正常核态缺页处理handle

        |

        '-- kmemcheck_fault (arch/x86/mm/kmemcheck)

            |

          |-- kmemcheck_access

          |     | (这里处理好确实挺不容易的,目前不完善)

          |     |-- kmemcheck_opcode_decode(读写解码器)

          |     |-- kmemcheck_read(见后面的分析)

          |     '-- kmemcheck_write (见后面的分析)

          |

          '-- kmemcheck_show目的是让产生缺页中断的指令完成

                |--设置PRESENT页属性

                '--禁中断, 置单步标志

 

进入缺页异常后要经过许多安全核查和判读,才能进入kmemcheck的缺页和单步异常回调函数,比如是否是来自核态空间的缺页异常,上次kmemcheck的处理是否结束等;不过在介绍主要函数之前,需要解释一个记录kmemcheck上下文的数据成员balance,此成员用来同步一对操作kmemcheck_show 和kmemcheck_hide,理论上,这对操作必须是平衡的,分别在缺页异常和单步异常中被执行。下面是缺页异常的调用逻辑:

 

  

单步异常:

do_debug (traps.c)

   '-- kmemcheck_trap(单步处理中首先检查有没有来自kmemcheck)

         |-- kmemcheck_active  (balance检查)

         |     

         '-- kmemcheck_hide目的:让下一次读写指令产生缺页中断

                |--kmemcheck_hide_all(置~PRESENT页属性)

                |

                '---清中断,去掉单步标志

因为中断标志为被设置成disable(这里各个处理器的架构不同,可能会导致kmemcheck的不同行为,移植时要小心),所以,cpu完成产生缺页中断的读写指令后,马上会进入单步异常处理句柄。下面是单步异常的调用逻辑:

 

 

 

退出单步异常之前,中断使能,并去掉单步标志,其唯一目的就是重新隐藏对应的页表,是位于该页表的其他核态地址也能够产生缺页中断。下面是kmemcheck_write的调用逻辑:

kmemcheck_write

 |  这里会考虑地址长度是否跨越页边界,

|  不过看起来没有那么成功

'--kmemcheck_write_strict

      |--kmemcheck_save_addr(addr);

      |

'--kmemcheck_shadow_set

(在对应的影子内存中设置成initialized)

 

 

下面是 kmemcheck_read的调用逻辑:

kmemcheck_read

   |

'--kmemcheck_read_strict

|--kmemcheck_save_addr(addr);

B--kmemcheck_shadow_test(shadow, size);

        |

|--kmemcheck_error_save

|--坚查是否设置了one shot

              '--kmemcheck_shadow_set

(在对应的影子内存中设置成initialized)

 

 

 

 

注意:实际上,代码还考虑了重复指令,但是看起来好像不是很成功;同时也考虑了跨越页边界地址的读写。本次试验报告没有给出这两类情况是因为当前的实现并不是很完善(更主要的是这两类问题涉及到许多非常细节的架构相关的内容)。不过在这里提到这些内容,是希望能够给读者提供全貌的kmemcheck,同时也寄希望于读者为开源社区做更多的贡献。

2.2.5内存管理hook(同时支持 slab和slub)

核心数据结构:page->shadow

核心代码位置mm/kmemcheck.c; mm/slab.c; mm/slub.c

概念解释:"工作内存"就是返回给caller并供caller使用的内存空间;"影子内存"用来记录工作内存的状态。

 

kmemcheck补丁在核态内存分配函数对(kmalloc和kfree)中分别插入了两个钩子,其主要目的有三个:

  • 1) 分配或释放影子内存;(用于记录工作内存的状态)
  • 2) 标识或清除"不存在"页表属性(pte中,能够触发缺页中断);
  • 3) 隐藏或者显示,和标识工作内存的状态于影子内存;

显然,struct page是Linux内存管理的页结构,kmemcheck补丁在此结构中为了记录影子内存的地址而新增加了一个成员page->shadow。在分配工作内存的同时也分配了相对应的影子内存。

下面两个框图分别是从kmalloc和kfree到kmemcheck的调用逻辑:

kmalloc

  '--__kmalloc

       '--slab_alloc

            |

            B--kmemcheck_slab_alloc (内存池)

            |    |    (标记成未初始化)

            |    '--kmemcheck_mark_uninitialized_pages

            |

            '--__slab_alloc (new allocation)

                 |

                 '--new_slab

                      '--allocate_slab

                          |

                          o--alloc_slab_page(分配工作内存)

                          |

                          '--kmemcheck_alloc_shadow

                               |--alloc_pages_node(分配影子内存)

                               |--kmemcheck_hide_pages(标记不存在和隐藏)

                               '--kmemcheck_mark_uninitialized_pages

                                                       (标记成未初始化)

                                  

kfree

  '--slab_free

       |

       B-- kmemcheck_slab_free 清除"initialized 标记"(内存池)

       |  对于从内存池直接分配的内存,只是简单的清除

        '--__slab_free

            '--discard_slab (new allocation)

                 '--free_slab

                      '--__free_slab

                          |

                          o--__free_pages (释放工作内存)

                          |

                          '--kmemcheck_free_shadow

                               |--kmemcheck_show_pages (去掉"不存在"、标记)

                               '--__free_pages(释放影子内存)

                                                     

                                 

 

 

  

注意: Kmalloc/kfree都有两个重要的分支,一个是从/到内存池,另一个是直接从/到Buddy分配器,因水平和篇幅问题,只能点到为止。

2.2.6 kmemcheck文档,内核选项配置。

这里内容相对简单,这里就不累赘了。

2.3 kmemcheck实现框架解析

下面从整体上分析kmemcheck的实现框架,首先介绍,由kmemcheck导入的,与系统相关的,新增加的数据结构定义:

1) 定义在pgtable.h中的两个宏

#define _PAGE_BIT_HIDDEN     11    /* hidden by kmemcheck */

#define pte_hidden(x)   ((x).pte_low & (_PAGE_HIDDEN))

显然,这两个宏与隐藏属性相关,而且x86CPU的内部页表(TLB)中肯定没有对应的属性位,kmemcheck在此处导入_PAGE_HIDDEN的唯一目的就是在缺页中断句柄(handle)中区分中断源是否是kmemcheck,起辅助区分作用。

2) 定义在slab.h中的SLAB_NOTRACK

/* Don't track use of uninitialized memory */

# define SLAB_NOTRACK          0x00800000UL

下面通过框图对比memory操作的标准流程[2],来说明kmemcheck的整个监控过程:

VS

 

 

标准内存活动过程

 

Malloc(size) //分配

不妨称之为"工作内存"

 

Read/write //操作

 

Free()//释放

 

 

 

Kmemcheck中的内存活动过程

Malloc(2xsize) 多了一倍,用来标记"工作内存"状态的shadow(影子内存);同时把工作内存所在的页面标设成(~_PAGE_PRESENT)"不存在"和"隐藏","不存在"是为了产生缺页中断,"隐藏"是为了区别真正的缺页中断。

 

Read/write //操作

对被标成"不存在"的任何一页内存读写,都会产生缺页中断(PageFault),kmemcheck在缺页中断句柄中的钩子函数分别对读和写作了不同的处理,如果是读操作则去检查影子内存中对应地址的状态,如果不是initialized,则产生un-initialized报告(同时还是把这些内存标记成initialized(相同的位置只报告一次));如果是写操作,则在对应的影子内存中设置成initialized状态。最后不管是读还是写都会做下面两件事情:

  • 1) 把这些地址页都设置成"存在",其目的是让page fault 返回后能够完成原来的读写操作。
  • 2) 关中断、设置单步标志。其目的是读写操作完成后,重新让这些地址所在的页标示成"不存在"

注意:隐藏标记是一直存在,直到该内存释放。

 

Free()//除了释放工作内存和影子内存以外,还要把那些内存标记present和no hidden.

 

 

 

 

 

  

注意,为了系统能够正常运行,关键的内核代码被两个重要的标记保护起来,他们是SLAB_NOTRACK, __GFP_NOTRACK,这两个开关参数在内存分配时就被kmalloc(kmalloc具体实现在slab.c或slub.c)用来屏蔽所有的kmemcheck调用。还有一个间接的保护开关__GFP_ZERO是用来分配时(也在slub.c或slab.c)就初始化内存,因此些内存在第一时间就被kmemcheck_shadow_set 标成"已初始化"了。

2.4验证过程

写了一个简单的内核模块,旨在验证kmemcheck的内存监控功能,同时也测试了kmemcheck用到的一些控制参数如:SLAB_NOTRACK、__GFP_NOTRACK、__GFP_ZERO。

 

基本验证逻辑:

首先分配一块内存,然后通过模块加载参数来控制是否初始化该内存,是否使用SLAB_NOTRACK, __GFP_NOTRACK等。因为测试代码比较简单,所以不再花费笔墨,详见附录。下图是测试用例的一小部分输出:

root@localhost:/tmp> insmod kmemcheck-test.ko  kmem_size=512

##kmemcheck_test_init kmalloc size =512 with No GRP_ZERO, kmemcheck track, non-initialized

##kmemcheck_test_init Addr= 0xf69e1400 not been initialized

##kmemcheck_test_init read 0xf69e1400= 0xf69e1600 total = 0x0

WARNING: kmemcheck: Caught 32-bit read from uninitialized memory (f69e1400)

Pid: 2302, comm: insmod Not tainted (2.6.27-15567-gfca4535-dirty #17) Latitude D610                  

EIP: 0060:[<f801c165>] EFLAGS: 00010246 CPU: 0

EIP is at kmemcheck_test_init+0x165/0x1ef [kmemcheck_test]

EAX: 00000000 EBX: f801a157 ECX: f73e6df4 EDX: f69e1400

ESI: f801c000 EDI: 00000000 EBP: f69f3f1c ESP: c04d47a8

DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068

CR0: 8005003b CR2: f69e1400 CR3: 373d9000 CR4: 000006d0

DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000

DR6: ffff4ff0 DR7: 00000400

 [<c010111a>] do_one_initcall+0x2a/0x140

 [<c013f058>] sys_init_module+0x88/0x1b0

 [<c01032ae>] syscall_call+0x7/0xb

 [<ffffffff>] 0xffffffff

 

 

  

第三章 总结分析

3.1 总结:
3.1.1 设计原理评价

综上所述,kmemcheck的设计原理是成立的,设计的整体逻辑是严密的;从验证结果观察,实验数据也是合理可靠的(见附录2)。这一点可以从Linux内核提交日志中得到佐证。(尽管kmemcheck没有进入Linux的mainline,但是下面这个命令可以帮助我们,在当前的mainline库提交日志中看到"contributions"描述):

#git log | grep -C1 kmemcheck(edited)

Vegard Nossum reported a bug which accesses freed memory.

kmemcheck reported this bug:

uninitialized memory references to the fast_start parameters detected by the kmemcheck utility.

3.1.2 x86体系下kmemcheck实现关键点

第二部分所述的影子内存、缺页、单步是kmemcheck实现的重要技术细节,但并不是实现的关键点,核态空间的小粒度4K页表支持是x86体系下kmemcheck实现的关键之处。也即引起缺页异常的页单位非常小,影响范围之有4K,这个特点恰恰取决于x86体系的4K TLB页特征属性。

3.1.3 系统稳定性评价

作为开源的测试工具,kmemcheck的系统稳定性是完全可以接受的;尽管系统在运行期间偶尔会出现内核崩溃或者死锁之类的情况,今年四月,Jeremy Andrews曾经贴出让kmemcheck进入mainline库的讨论(参考文献),但是到目前为止还了无音信,部分原因可能与此有关。

3.1.4 不稳定原因初探

其实,在kmemcheck的调研过程中,感觉到它的具体实现过程,与内存子系统、调度子系统,进程管理,中断处理等其他子系统的关系非常紧密。下面对系统的不稳定性略作推测: 

  • 1) 错误报告系统的协调问题。目前错误报告系统是异步的,就是说发现内存不当使用后,先把错误上下文先记录到缓存,然后恰当的时候推出报告;目前kmemcheck在发现(缓存错误上下文)和报告(退出错误上下文)之间的缓存同步可能还不是非常健壮(robust)。
  • 2) 重复读写指令处理不完善。
  • 3) 内存管理本身的缺陷(有slab和slub本身bug,压力测试中spin_lock可能的自锁现象)。

当然,要把握好上面的每个内容都不是轻松的事,更何况需要这些系统协同考虑。正如同事物发展,代码演进也是一个过程,开源社区的特点就是物尽其用、人尽其材。。

3.1.5 可能的发展方向

目前的kmemcheck代码基本上没有考虑SMP、抢占、实时抢占,当然实现这些特征只是时间问题。另外,在此提出这个问题并非吹毛求疵,而是因为抢占(preempt)已经是2.6内核的一个基本特征了;仅此提醒对kmemcheck研究有兴趣的各位同仁。

3.2 结论分析
3.2.1其他架构kmemcheck的可行性分析

综合3.1.2节的分析,尽管诸如powerpc E500之类的CPU也有256个入口4K大小的二级TLB管理,但是几乎所有的嵌入式系统都会把核态空间映射到一个入口中,或者是几个(如arm),他们的目的几乎是一致的:尽量减少核态空间的TLB切换。所以结论如下:如果没有小粒度的硬件页表支持,kmemcheck几乎是不可能移植到该架构上去的;否则理论上应该是可以的,只是工作量非常大,稳定性有待验证,如此而已。

3.2.2思维突破

本人分析kmemcheck的主要原因并不仅仅是kmemcheck原理、功能、或者设计技巧之类的实现细节,更重要的是kmemcheck的作者Vegard Nossum非常了不起地突破了两个思维惯性:

1)消耗将近一半的内存跟踪另一半内存的状态;

2)每一条读写指令都会产生一个缺页和单步异常中断。

选择这样的设计方案需要一定的勇气,并非常清晰地要求主体从日常的产品设计思维中逃离出来;另外从意识角度讲,思维撞到墙了以后怎么办?这种情况在产品设计或者错误调试中经常会遇到,有时候比较幸运,有时候墙的厚度和高度令人难以逾越。

附录:

附录一、Kmemcheck的简单测试用例:

/*

 * mm/kmemcheck_test.c - simple functional sanity test on kmemcheck

 *

 * Copyright (C) 2008, Windriver

 * Author Zumeng Chen <zumeng.chen@windriver.com>

 */

 

#include <linux/sched.h>

#include <linux/gfp.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kmemcheck.h>

 

/* Usage:

 *    "insmod kmemcheck-test.ko kmem_initialized_flag=1" will not trigger kmemcheck

 *    "insmod kmemcheck-test.ko" will trigger kmemcheck to report un-initialized access.

 */

static int kmem_initialized_flag = 0;

module_param(kmem_initialized_flag, int, 0);

MODULE_PARM_DESC(kmem_initialized_flag, "initialization controller flag");

 

static int kmem_gfp_zero_flag = 0;

module_param(kmem_gfp_zero_flag, int, 0);

MODULE_PARM_DESC(kmem_gfp_zero_flag, "test GFP_ZERO flag");

 

static int kmem_gfp_notrack_flag = 0;

module_param(kmem_gfp_notrack_flag, int, 0);

MODULE_PARM_DESC(kmem_gfp_notrack_flag, "test GFP_NOTRACK flag");

 

static int kmem_size = 4096;

module_param(kmem_size, int, 0);

MODULE_PARM_DESC(kmem_size, "kmalloc size");

 

static int * bufp = NULL;

 

static int __init kmemcheck_test_init(void)

{

       gfp_t gfp_flags = GFP_KERNEL;

       int i = 0,total = 0;

      

       if(!kmemcheck_enabled) {

              printk("##%s kmemcheck is not enabled /n",__func__);

              printk("##%s please rmmod this module /n",__func__);

              printk("##%s echo 1 >/proc/sys/kernel/kmemcheck/n",__func__);

              return 0;

       }

 

       printk("##%s kmalloc size =%d with %s, %s, %s/n",__func__,kmem_size,

              kmem_gfp_zero_flag ? "GFP_ZERO" : "No GFP_ZERO",

              kmem_gfp_notrack_flag? "GFP_NOTRACK" : "kmemcheck track",

              kmem_initialized_flag? "initialized" : "non-initialized");

 

       if(kmem_gfp_zero_flag)

              gfp_flags |= __GFP_ZERO;

 

       if(kmem_gfp_notrack_flag)

              gfp_flags |= __GFP_NOTRACK;

             

       bufp = (int *)kmalloc(kmem_size, gfp_flags);

 

       /*To test if kmemcheck works well using kmem_initialized_flag*/

       if(kmem_initialized_flag == 1)  {

              printk("##%s Addr= 0x%x has been initialized/n",__func__,bufp);

              memset(bufp, 0x0,kmem_size);

       }else

              printk("##%s Addr= 0x%x not been initialized/n",__func__,bufp);

 

       printk("##%s read 0x%x= 0x%x total = 0x%x/n",__func__,

                     (unsigned int)bufp,*bufp,total);

 

       /*to acces new memory implied on kmem_initialized_flag*/

       for(i=0 ; i< kmem_size;i++)

              total += *(bufp+i);

 

       printk("##%s read 0x%x= 0x%x total = 0x%x/n",__func__,

                     (unsigned int)bufp,*bufp,total);

       return 0;

}

 

module_init(kmemcheck_test_init);

 

static void __exit kmemcheck_test_exit(void)

{

       printk("#######%s module exit /n",__func__);

       if(!kmemcheck_enabled) {

              printk("##%s kmemcheck is not enabled /n",__func__);

              return ;

       }

       printk("##%s read 0x%x= 0x%x/n",__func__,(unsigned int)bufp,*bufp);

       *bufp = 0xaa55aa55;

       printk("##%s read 0x%x= 0x%x/n",__func__,(unsigned int)bufp,*bufp);

       kfree(bufp);

       return ;

}

module_exit(kmemcheck_test_exit);

 

MODULE_AUTHOR("Wind River");

MODULE_DESCRIPTION("Kmemcheck functional test");

MODULE_LICENSE("GPL");

 

附录二、Kmemcheck测试用例实验结果:

root@localhost:/tmp> insmod kmemcheck-test.ko  kmem_size=256

##kmemcheck_test_init kmalloc size =256 with No GRP_ZERO, kmemcheck track, non-initialized

###mark uninitialized###kmemcheck_slab_alloc 118 object =0xf6951a00 

##kmemcheck_test_init Addr= 0xf6951a00 not been initialized

##kmemcheck_test_init read 0xf6951a00= 0xf6951900 total = 0x0

WARNING: kmemcheck: Caught 32-bit read from uninitialized memory (f6951a00)

Pid: 2294, comm: insmod Not tainted (2.6.27-15567-gfca4535-dirty #17) Latitude D610                  

EIP: 0060:[<f801c165>] EFLAGS: 00010246 CPU: 0

EIP is at kmemcheck_test_init+0x165/0x1ef [kmemcheck_test]

EAX: 00000000 EBX: f801a157 ECX: f73e6df4 EDX: f6951a00

ESI: f801c000 EDI: 00000000 EBP: f69f3f1c ESP: c04d4568

 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068

CR0: 8005003b CR2: f6951a00 CR3: 36950000 CR4: 000006d0

DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000

DR6: ffff4ff0 DR7: 00000400

 [<c010111a>] do_one_initcall+0x2a/0x140

 [<c013f058>] sys_init_module+0x88/0x1b0

 [<c01032ae>] syscall_call+0x7/0xb

 [<ffffffff>] 0xffffffff

 

root@localhost:/tmp> insmod kmemcheck-test.ko kmem_size=64

##kmemcheck_test_init kmalloc size =64 with No GRP_ZERO, kmemcheck track, non-initialized

###mark uninitialized###kmemcheck_slab_alloc 118 object =0xf6916040 

##kmemcheck_test_init Addr= 0xf6916040 not been initialized

##kmemcheck_test_init read 0xf6916040= 0xf6916100 total = 0x0

WARNING: kmemcheck: Caught 32-bit read from uninitialized memory (f6916040)

Pid: 2170, comm: insmod Not tainted (2.6.27-15567-gfca4535-dirty #17) Latitude D610                  

EIP: 0060:[<f801c165>] EFLAGS: 00010246 CPU: 0

EIP is at kmemcheck_test_init+0x165/0x1ef [kmemcheck_test]

EAX: 00000000 EBX: f801a157 ECX: f73e6df4 EDX: f6916040

ESI: f801c000 EDI: 00000000 EBP: f69f3f1c ESP: c04d40e8

 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068

CR0: 8005003b CR2: f6916040 CR3: 373f1000 CR4: 000006d0

DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000

DR6: ffff4ff0 DR7: 00000400

 [<c010111a>] do_one_initcall+0x2a/0x140

 [<c013f058>] sys_init_module+0x88/0x1b0

 [<c01032ae>] syscall_call+0x7/0xb

 [<ffffffff>] 0xffffffff

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值