首页 > 代码库 > 一个数据类型不匹配引发的coredump“血案”

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

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

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

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  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在CODE上查看代码片派生到我的代码片
 
  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. …  
  9. }  

 

 

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

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

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

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  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++开发,但对于服务器上的高并发程序开发,汇编语言也是必要的。

 

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