kdump使用实例一:内存越界

Kernel Panic (Kdump) 解析实例之一
原创Red-White-Blue 最后发布于2013-09-27 23:02:29 阅读数 4256  收藏

网络上已经有一些介绍如何配置kexec已产生kdump的文章。这里不重复介绍配置方法,而是介绍如何进行kdump文件解析。

下面介绍的都是Linux内核产生的kdump,利用crash这一工具解析。关于crash这个工具支持哪些命令,请参考crash命令自带的help。

1) 用crash命令打开vmcore
刚打开文件后,会输出一些关于软件本身以及vmcore文件的信息,基本用处不大。
[root@node-8 log]# crash vmlinux  vmcore

     gdb本身的版本等信息,省略。

2) 用bt命令查看导致panic的函数调用栈及当时的寄存器内容
注意,我们只需要看到[exception RIP...]以及其调用者即可。比如,下面的例子中,[exception RIP]对应#5,那么#0至#4只是系统在执行到引起panic的代码后的例行操作,本身对于解panic问题没有帮助。

RIP是当前要执行的指令的地址。

从这里我们可以看到的是:系统在执行scst_do_job_active这个函数调用的list_del()时发生了panic。具体地址是list_del+27。

crash> bt
PID: 60146  TASK: ffff880175c80ab0  CPU: 2   COMMAND: "abd"
 #0 [ffff8801759a3ba0] machine_kexec at ffffffff810368e9
 #1 [ffff8801759a3c00] crash_kexec at ffffffff810b8ad8
 #2 [ffff8801759a3cd0] oops_end at ffffffff814b9c60
 #3 [ffff8801759a3d00] die at ffffffff8101732b
 #4 [ffff8801759a3d30] do_general_protection at ffffffff814b97d2
 #5 [ffff8801759a3d60] general_protection at ffffffff814b8fa5
    [exception RIP: list_del+27]
    RIP: ffffffff812680eb  RSP: ffff8801759a3e10  RFLAGS: 00010046
    RAX: dead000000100100  RBX: ffff880093ec5950  RCX: 0000000000000030
    RDX: 000000000000c78b  RSI: 0000000000000000  RDI: ffff880093ec5950
    RBP: ffff8801759a3e20   R8: ffff880093ec5950   R9: 0000000000000080
    R10: 0000000000000000  R11: 0000000000000010  R12: 0000000000000000
    R13: ffff880177076bd0  R14: ffff880177076bc8  R15: 0000000000000000
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #6 [ffff8801759a3e28] scst_do_job_active at ffffffffa0422516 [scst]
 #7 [ffff8801759a3e68] scst_cmd_thread at ffffffffa042282a [scst]
 #8 [ffff8801759a3ee8] kthread at ffffffff810916c6
 #9 [ffff8801759a3f48] kernel_thread at ffffffff810141ca
 


3) 反汇编确定哪个寄存器是入口参数
从bt命令的结果可以看出,导致panic的指令是list_del+27。 list_del的源代码如下(kernel 2.6.32):

 44 void list_del(struct list_head *entry)
 45 {
 46         WARN(entry->prev->next != entry,
 47                 "list_del corruption. prev->next should be %p, "
 48                 "but was %p\n", entry, entry->prev->next);
 49         WARN(entry->next->prev != entry,
 50                 "list_del corruption. next->prev should be %p, "
 51                 "but was %p\n", entry, entry->next->prev);
 52         __list_del(entry->prev, entry->next);
 53         entry->next = LIST_POISON1;
 54         entry->prev = LIST_POISON2;
 55 }


我们用dis(disassemble)命令对list_del这个内核函数进行反汇编(绿色为注释,==>代表赋值。):

crash> dis list_del

0xffffffff812680d0 <list_del>:  push   %rbp

0xffffffff812680d1 <list_del+1>:        mov    %rsp,%rbp  ;前两句是标准的函数入口操作
0xffffffff812680d4 <list_del+4>:        push   %rbx            ;%rbx是 callee-save register,使用前先压栈保存.
0xffffffff812680d5 <list_del+5>:        mov    %rdi,%rbx  ;%rdi ==> %rbx; 因为%rdi不是 callee-save register。%rdi的对应如可参数entry
0xffffffff812680d8 <list_del+8>:        sub    $0x8,%rsp    ;栈顶减8,给WARN使用的局部变量预留空间
0xffffffff812680dc <list_del+12>:       mov    0x8(%rdi),%rax  ;entry->prev ==> %rax
0xffffffff812680e0 <list_del+16>:       mov    (%rax),%r8   ;entry->prev->next ==> %r8
0xffffffff812680e3 <list_del+19>:       cmp    %r8,%rdi      ;比较 entry->prev->next 和 entry
0xffffffff812680e6 <list_del+22>:       jne    0xffffffff81268121 <list_del+81> ; 如果二者不相等,则跳转,对应WARN(entry->prev->next != entry,...);
0xffffffff812680e8 <list_del+24>:       mov    (%rbx),%rax  ;entry->next ==> %rax。注意,<list_del+5>这一行已经把entry赋值给%rbx。使用%rbx而不是%rdi,因为可能从别的地方跳转到这一行,%rdi已经失效。
0xffffffff812680eb <list_del+27>:       mov    0x8(%rax),%r8; entry->next->prev ==> %r8,由于RIP显示就panic在这一句,而这一句对应了WARN(entry->next->prev != entry,...); 说明entry->next不是合法的指针。

根据上面的汇编分析,入口参数(entry)在rbx里面保存了。rbx的值为ffff880093ec5950。我们看看entry的内容,从而就能知道entry->next是什么。

crash> kmem ffff880093ec5950  ; 获取%rbx对应地址的内存分配信息,是一个scst_cmd结构体,与上面的bt信息吻合
CACHE            NAME                 OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE
ffff880195443240 scst_cmd                 528        586      1155    165     4k
SLAB              MEMORY            TOTAL  ALLOCATED  FREE
ffff880093ec50c0  ffff880093ec5110      7          3     4
FREE / [ALLOCATED]
   ffff880093ec5950  (cpu 4 cache)


      PAGE        PHYSICAL      MAPPING       INDEX CNT FLAGS
ffffea00024fb140  93ec5000                0        0  1 20000000000080


由于 scst_cmd的第一个成员就是list_head,直接把ffff880093ec5950作为list_head显示


crash> struct list_head ffff880093ec5950
struct list_head {
  next = 0xdead000000100100, 
  prev = 0xffff880177076bd0
}


参见内核中的注释,#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
LIST_POISON1 即为 0xdead000000100100


从上面的内容可以看出,该list_head的内容是有问题的,其next表明它已经被从list上删除,但是其prev表明它还在list上。因此,可以认为程序没有保障list_head操作的原子性导致的。

猜测是list插入与删除并发引起的问题。具体的原因是由于锁的使用问题,就不在这里讨论了。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值