一个数据类型不匹配引发的coredump“血案”

前段在开发中遇到了测试组报过来的程序coredump 问题,stack如下: (Linux X86-64位系统,RHEL6,隐去程序名字更名为APP)

Stack: [0x0000000030074000,0x0000000030a75000], sp=0x0000000030a73830, free space=10238k

[plain] view plaincopy

  1. Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
  2. C  [APP+0x4b6768]  ACRManager::getLastFailOverEventTS(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*)+0x2ae
  3. C  [APP+0xacefe9]  INCACRSessionData::INCACRSessionData(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, int, std::string)+0x369
  4. C  [APP+0xad193c]  INCACRSessionDataMap::INCACRSessionDataMap(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, std::string)+0xd4
  5. C  [APP+0x4b52e6]  ACRManager::generateINCfile(std::vector<PreACRRec*, std::allocator<PreACRRec*> >&, PreACRRec*, int, std::string)+0x162
  6. C  [APP+0x5002b6]  Audit::closeACR(std::string const&, std::vector<PreACRRec*, std::allocator<PreACRRec*> >, DBConn*, int, int)+0x3c8
  7. C  [APP+0x4ffbd5]  Audit::processACR(std::string const&, int)+0x55b
  8. C  [APP+0x4ff4b2]  Audit::acr_audit_process()+0x338
  9. C  [APP+0x4fcf65]  Audit::acrStart()+0xcf
  10. C  [APP+0x50195f]  acr_thread_run+0x54

程序 没有加入-g选项,看不到对应的行号。只能使用objdump -Cd 生成反汇编代码,然后对着看。

红色标出的就是coredump对应的那行的汇编语言

00000000008b64ba<ACRManager::getLastFailOverEventTS(std::vector<PreACRRec*,std::allocator<PreACRRec*> >&, PreACRRec*)>:

8b64ba:    55                      push   %rbp

8b64bb:   48 89 e5                mov    %rsp,%rbp

8b64be:   41 56                   push  %r14

…..

8b6760:   e8 39 9a e9 ff          callq  75019e <std::vector<PreACRRec*,std::allocator<PreACRRec*> >::operator[](unsigned long)>

8b6765:   48 8b 00                mov    (%rax),%rax

8b6768:   8b 40 34                mov    0x34(%rax),%eax   -> 0x8b64ba+0x2ae

8b676b:   83 c0 01                add    $0x1,%eax

8b676e:   41 39 c4                cmp    %eax,%r12d

结合C++代码,发现出问题的是下面if判断中!=号后面的取成员变量的语句:

[cpp] view plaincopy

  1. int curPos = 0;
  2. int nextPos = curPos + 1;
  3. for (int i=0; i<=acrInDB.size()-2; i++)
  4. {//forexample:5,7....
  5. if (acrInDB[curPos]->Accounting_Record_Number != acrInDB[nextPos]->Accounting_Record_Number+1)
  6. {
  7. AdditionalChargingInfo*acrPP=acrInDB[curPos]->acr_processing_parameter;
  8. }

这段代码不是我写的,看到这个问题,我首先联想到是不是多线程访问acrInDB变量出现了问题。

经查代码发现,acrInDB是个局部变量,排除了多线程的问题。那么,这行简单的语句,到底出现了什么问题,会导致coredump呢?

仔细看看上下文,下面的这行代码具有很高的嫌疑——当acrInDB.size() == 1时,程序可能会出现非预期的行为。

[cpp] view plaincopy

  1. for (int i=0; i<=acrInDB.size()-2; i++)

问题是否真的和猜测的一样呢?让我们深入到汇编代码中寻找答案。注意看我后面添加的注释,是对应的c++代码行

-----------------------------------------------------------------------------------------------

8b69fe:     8b 45 cc               mov    -0x34(%rbp),%eax    // this line is int i = 0;

8b6a01:     4c 63 e0               movslq%eax,%r12  // convert it to 8-byte long (unsignedlong) to compare with acrInDB.size()

8b6a04:     48 8b 85 70 ff ff ff   mov    -0x90(%rbp),%rax

8b6a0b:     48 89 c7               mov   %rax,%rdi

8b6a0e:     e8 61 97 e9 ff         callq  750174 <std::vector<PreACRRec*,std::allocator<PreACRRec*> >::size() const>

8b6a13:     48 83 e8 02            sub    $0x2,%rax //acrInDB.size() - 2

8b6a17:     49 39 c4               cmp   %rax,%r12    //i<=acrInDB.size()-2

8b6a1a:     0f 96 c0               setbe  %al  // get the result

太高兴了,在最后一行看到了一个setbe指令,这个指令的具体作用如下:(参考intel汇编指令手册)

setbe / setna D Set if below or equal (unsigned) CF|ZF 187

确实是按照unsigned类型去做比较的。也就是说,<=号两边的数据都要转成unsigned然后比较。当acrInDB.size() == 1的时候,<=号右边是-1,转成unsigned
则变成了最大的无符号整数,这样就进入了这个for循环。然后nextPos是1,在访问acrInDB[1]的时候,相当于访问了一个random的内存地址,导致出现了SIGSEGV,
程序coredump。

再回头看看core文件,出错指令:

8b6768:    8b 40 34                mov    0x34(%rax),%eax

是将MEM[%RAX+0x34]放到EAX寄存器中,我们使用info register命令看一下RAX寄存器的值,RAX=0x646f4d6e6f697463,非常明显,是一个非法的内存地址,访问则会SIGSEGV。

调试总结:

1. 在做数据比较时,类型匹配那是必须的。

2. 编译时加入-g选项。

3. 熟悉X86-64上的汇编,虽然是C++开发,但对于服务器上的高并发程序开发,汇编语言也是必要的。

 

时间: 2024-10-11 21:29:11

一个数据类型不匹配引发的coredump“血案”的相关文章

一个换算小程序引发的测试血案

请原谅我的标题写的有些夸张,但实际情况往往比想象的更离奇.好了进入主题.小A最近正在学习C语言,这时候小A上小学的儿子小B跑过来问:"爸爸,这道数学题怎么算?",小A看了看题目说"一百天是多少个礼拜零多少天?" 好了,我们的小A,看到题目后不假思索,一迅雷不及掩耳盗铃之势写下了如下代码: #include <stdio.h> int main(void) { int days; int week,day; printf("please in p

本地Host和域名不匹配引发的血案

本地Host和域名不匹配引发的血案 一.问题及原因分析: 1.页面无法加载,看控制台,发现这些do加载JS文件时找不到. 2.代码中确认对应Do加载时引用的JS路径(即域名或IP: hdm.oper.jd.com ): 3.在Host中找到对应的域名和IP的映射,删除,然后发现页面正常: 二.解决方法: 方法一:代码中直接将JS文件的引用路径改为本地: 方法二:更改Host,使加载js文件时的域名解析到一个可用的正常IP: 三.说明: 这个问题不难,只是自己缺少前台经验,不知道可在浏览器的控制台

正则匹配引发的血案

引子:一家商业IT服务公司,提供给客户的服务突然中断了将近一个小时,事后排查原因,竟然是因为一个正则表达式引起的,小小的正则表达式何以引起如此严重的问题? 事情的原因是由于正则解析导致cpu资源消耗殆尽,引起连锁反应,后续的服务都无法对外提供.引起故障的正则表达式是这样的,“(?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|

[转]access 标准表达式中数据类型不匹配

好久没有用access,今儿遇到一个特别让人无语的问题: access数据表的Date/Time类型的字段,假如字段名为dtime: 如果直接用dtime=‘2013/9/6 10:50:21’,sql语句会报错-->“标准表达式中数据类型不匹配”, 如果用dtime like ‘%2013/9/6 10:50:21%’,sql语句依旧会报错-->“标准表达式中数据类型不匹配”,网上还有哥们儿说了,日期用# #如:dtime=#2013/9/6 10:50:21%#,dtime like #2

一个由IsPrime算法引发的细节问题

//******************************* // //    2014年9月18日星期四,于宿舍撰写 //    作者:夏华林 // //******************************** 好久没有没有更新博客了,最近确实烦心事儿挺多,已经大三了,真的静下心来好好看看书了. 今天要说的,就是一个由IsPrime算法引发的细节问题,我这里说的细节,是我所认为的,若有不妥,望指正! 一个简单的IsPrime算法的实现如下: 1 bool IsPrime(int

一个截取字符串函数引发的思考

背景 前些天,遇到这样一个问题,问题的内容如下: 要求编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串.但是要保证汉字不被截半个,如"我ABC", 4,截取后的效果应该为"我AB",输入"我ABC汉DEF", 6,应该输出为"我ABC",而不是"我ABC+汉的半个". 问题 刚看到这个问题的时候,以为还是很简单的,但写出来之后,发现并不是想要的效果.回想一下当时的思路,就发现刚开

为什么我们要在指针前面加一个数据类型来限定那?

原因是指针只是指定了内存单元的首地址,但并不知道长度是多少? 所以需要一个数据类型来指定.比如 int *p 就是取出从首地址开始4个字节. 还有就是malloc返回是需要是一个无类型的地址. 这时我们需要强制类型转换,来告诉我们从这个地址开始,以多大空间来解析 比如4个字节 .

QVariant(相当于是Java里面的Object,起到一个数据类型“擦除”的作用,可以使用Q_DECLARE_METATYPE进行注册)

=QVariant= [%这个类型相当于是Java里面的Object,它把绝大多数Qt提供的数据类型都封装起来,起到一个数据类型“擦除”的作用.比如我们的 table单元格可以是string,也可以是int,也可以是一个颜色值,那么这么多类型怎么返回呢?于是,Qt提供了这个QVariant类型,你可 以把这很多类型都存放进去,到需要使用的时候使用一系列的to函数取出来即可.比如你把int包装成一个QVariant,使用的时候要用 QVariant::toInt()重新取出来.这里需要注意的是,Q

C# 一个简单的秒表引发的窗体卡死问题

一个秒表程序也是我的一个心病,因为一直想写这样的一个东西,但是总往GUI那边想,所以就比较怵,可能是上学的时候学MFC搞出的后遗症吧,不过当我今天想好用Win Form(话说还是第一次写win form)写这么一个东西的时候,居然so easy. 所以说,做不了不可怕,怕的是你不去做,因为你不去做,你就永远不知道你能不能做它.事实证明,大部分你犹豫能不能做的事情,实际上你都能搞定. 虽然成功实现了一个秒表的简单功能,即开始计时和停止.但是却引发了一个关于win form和C#线程的问题. 下面一