segfault段错误问题修复

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

内容简介: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段错误问题修复》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Head First Python(中文版)

Head First Python(中文版)

巴里(Barry.P.) / 林琪 等 / 中国电力出版社 / 2012-3-1 / 68.00元

你想过可以通过一本书就学会Python吗?《Head First Python(中文版)》超越枯燥的语法和甩法手册,通过一种独特的方法教你学习这种语言。你会迅速掌握Python的基础知识,然后转向持久存储、异常处理、Web开发、SQLite、数据加工和lGoogle App Engine。你还将学习如何为Android编写移动应用,这都要归功于Python为你赋予的强大能力。本书会提供充分并且完备......一起来看看 《Head First Python(中文版)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具