segfault段错误问题修复

栏目: 服务器 · 发布时间: 7年前

内容简介:segfault段错误是软件开发中经常会遇到的错误,该错误是由非法内存访问造成的,如空指针引用;在只读内存区域进行写操作;访问受保护的内存区域等。在不同场景下,segfault的解决难度大不相同,比如有源代码并且segfault很容易复现,重新编译一个调试版可执行文件,用gdb调试马上就能定位问题。但是如果segfault很难复现,或者没有调式版可用呢?又如何去定位问题呢?最近就在线上环境遇到一个qemu-ndb造成的segfault错误,影响很严重。qemu-nbd要用到nbd内核模块,segfault

segfault段错误是软件开发中经常会遇到的错误,该错误是由非法内存访问造成的,如空指针引用;在只读内存区域进行写操作;访问受保护的内存区域等。在不同场景下,segfault的解决难度大不相同,比如有源代码并且segfault很容易复现,重新编译一个调试版可执行文件,用gdb调试马上就能定位问题。但是如果segfault很难复现,或者没有调式版可用呢?又如何去定位问题呢?

最近就在线上环境遇到一个qemu-ndb造成的segfault错误,影响很严重。qemu-nbd要用到nbd内核模块,segfault错误出现时,qemu-nbd相关进程卡主无响应,造成业务无法正常运行,甚至强制kill掉qemu-nbd进程时,kill进程也会卡主,只能断电重启服务器使业务临时恢复正常运行。必须从根本上解决该问题,才能避免一些不必要的麻烦。

在这种场景下,遇到了以下几个难点:

  1. 难以复现,线上几十台服务器大约每隔1周有1~2台上qemu-nbd会出现segfault错误,几乎无法通过手工方式复现;
  2. 有源代码,但是在本地测试调式版qemu-nbd,性能极差,无法投入线上使用,否则影响线上业务正常运行;
  3. 线上运行的qemu-nbd在编译时加了 -O2 -fomit-frame-pointer 优化参数,增加了反汇编代码的分析难度;
  4. 线上运行的qemu-nbd符号表信息被strip掉了,进一步增加了反汇编代码的分析难度;
  5. segfault错误出现时在/var/log/message中只有一行日志信息,没有堆栈等更详细的信息。
kernel: qemu-nbd[13647]: segfault at 100 ip 0000561c8817d1c7 sp 00007fc8f1ce9c00 error 4 in qemu-nbd[561c88107000+155000]

也就是说,只能通过这一行日志信息,加上qemu-nbd二进制文件和源代码,去定位具体哪一行代码造成了segfault。

首先看下segfault日志的含义:

qemu-nbd[13647]                    可执行文件名[进程ID]
at 100                             出错时访问的内存地址
ip 0000561c8817d1c7                出错时的指令指针
sp 00007fc8f1ce9c00                出错时的栈指针
error 4                            错误码
qemu-nbd[561c88107000+155000]      进程虚拟内存区域(VMA)对应的文件名[VMA起始地址+VMA大小]

错误码的定义:

/*
 * Page fault error code bits:
 *
 *   bit 0 ==    0: no page found   1: protection fault
 *   bit 1 ==    0: read access     1: write access
 *   bit 2 ==    0: kernel-mode access  1: user-mode access
 *   bit 3 ==               1: use of reserved bit detected
 *   bit 4 ==               1: fault was an instruction fetch
 *   bit 5 ==               1: protection keys block access
 */
enum x86_pf_error_code {

    PF_PROT     =       1 << 0,
    PF_WRITE    =       1 << 1,
    PF_USER     =       1 << 2,
    PF_RSVD     =       1 << 3,
    PF_INSTR    =       1 << 4,
    PF_PK       =       1 << 5,
};

由此可见,错误码4(100)表示从用户态读时出现no page found。

生成反汇编代码:

$ objdump -d -M intel qemu-nbd > qemu-nbd.asm

有了以上信息,即可通过ip指令指针在反汇编代码中定位出错指令。指令地址:

0x0000561c8817d1c7 - 0x561c88107000 = 0x761c7

由于线上运行的qemu-nbd的符号信息被strip掉了(为了使发布的可执行文件尽可能小,并增加逆向难度,一般会将符号信息剔除掉),所以从反汇编代码中很难确定0x761c7指令地址到底对应的是源代码的哪一行。

不过万幸的是,当初编译xen时的编译环境还在,没有strip掉符号信息的qemu-nbd版本还在,位于 tools/qemu-xen-dir/qemu-nbd ,strip版本位于 dist/install/usr/local/lib/xen/bin/qemu-nbd 。现在对比下非strip和strip版本的反汇编代码:

$ vim -d qemu-nbd.asm qemu-nbd-strip.asm

segfault段错误问题修复

有符号信息对于定位源代码帮助非常大,由汇编代码可以看出问题出在函数 bdrv_co_flush ,执行指令 mov rax,QWORD PTR [rdx+0x100] 时出错,此时rdx为0,所以segfault日志信息中才有 at 100 。进一步分析汇编代码,确定rbx为函数 bdrv_co_flush 的第一个参数 BlockDriverState *bs ,rdx为 bs->drv ,rdx+0x100为 bs->drv->bdrv_co_flush 。至此可以确定segfault是由于 bs-drv 为NULL造成的,即产生了空指针引用。

2292     /* Write back all layers by calling one driver function */
2293     if (bs->drv->bdrv_co_flush) {
2294         ret = bs->drv->bdrv_co_flush(bs);
2295         goto out;
2296     }

关键函数及数据结构:

根据数据结构及内存对齐,确定 [rbx+0x38]bs->drv 即rdx, [rdx+0x100]bs->drv->bdrv_co_flush

确定了引起segfault的具体代码,问题就很好解决了。其时qemu主分支对此问题已经有一定修复, block: Guard against NULL bs->drv

如果能进一步深入分析引起 bs->drv 为空指针的原因,并能手动复现,找出攻击路径,即可发起DOS(拒绝服务)攻击。


以上所述就是小编给大家介绍的《segfault段错误问题修复》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Art and Science of Java

The Art and Science of Java

Eric Roberts / Addison-Wesley / 2007-3-1 / USD 121.60

In The Art and Science of Java, Stanford professor and well-known leader in CS Education Eric Roberts emphasizes the student-friendly exposition that led to the success of The Art and Science of C. By......一起来看看 《The Art and Science of Java》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具