上面一种情况是自己内存写超了,写到了用户态别的结构所在的内存,特点就是core文件显示 别处对象的内容乱七八糟,且一般是在对象析构时发生,这次讲解一下,自己把自己写坏的情况。
1
/**
2 *\author peakflys
3 *\brief 堆栈崩溃问题
4 */
5 #include <iostream>
6
using namespace std;
7
8
struct Temp
9 {
10
int a;
11 unsigned
char b[4];
12 };
13
14
class Test
15 {
16
public:
17
void temp();
18
private:
19
void fill(unsigned
char *data);
20 Temp tt;
21 };
22
void Test::fill(unsigned
char *data)
23 {
24
for(
int i=0;i<20;++i)
25 {
26 data[i] = i;
27 cout<<"data["<<i<<"]:\t"<<(
void *)&data[i]<<endl;
28 }
29 }
30
void Test::temp()
31 {
32 Temp t;
33 fill((unsigned char *)&t);
34 }
35
36 int main()
37 {
38 Test *pt = new Test();
39 pt->temp();
40 delete pt;
41 return 0;
42 }
运行结果:
data[0]: 0x7fffa4a38c60
data[1]: 0x7fffa4a38c61
data[2]: 0x7fffa4a38c62
data[3]: 0x7fffa4a38c63
data[4]: 0x7fffa4a38c64
data[5]: 0x7fffa4a38c65
data[6]: 0x7fffa4a38c66
data[7]: 0x7fffa4a38c67
data[8]: 0x7fffa4a38c68
data[9]: 0x7fffa4a38c69
data[10]: 0x7fffa4a38c6a
data[11]: 0x7fffa4a38c6b
data[12]: 0x7fffa4a38c6c
data[13]: 0x7fffa4a38c6d
data[14]: 0x7fffa4a38c6e
data[15]: 0x7fffa4a38c6f
data[16]: 0x7fffa4a38c70
data[17]: 0x7fffa4a38c71
data[18]: 0x7fffa4a38c72
data[19]: 0x7fffa4a38c73
段错误 (core dumped)
堆栈情况:
Program terminated with signal 11, Segmentation fault.
[New process 18836]
#0 0x0000000000400a3e in main ()
(gdb) bt
#0 0x0000000000400a3e in main ()
(gdb)
总结:temp成员函数在调用fill成员函数时通过thiscall方式调用,gcc具体实现是把this指针像普通参数一样入栈传过去,而VS是在定参的情况下,this指针通过ECX传入,这个就是这次悲剧的祸根,通过gdb打印出来的情况是:
(gdb) info fr
Stack level 0, frame at 0x7fff49980a50:
rip = 0x400975 in Test::fill(unsigned char*) (test.cpp:22); saved rip 0x400a56
called by frame at 0x7fff49980a80
source language c++.
Arglist at 0x7fff49980a40, args: this=0x601280, data=0x3d9da611a0 "\203�\001\031�f\203;"
Locals at 0x7fff49980a40, Previous frame's sp is 0x7fff49980a50
Saved registers:
rbp at 0x7fff49980a40, rip at 0x7fff49980a48
(gdb) p &this
$7 = (Test * const *) 0x7fff49980a20
(gdb) p &data
$8 = (unsigned char **) 0x7fff49980a18
通过对data的持续写最终导致栈地址写到了this指针所在的栈内存,这样再从那块地址取this指针时,取的就不是this指针,最终从未知的“that指针”取数据,十有八九程序就挂了。
这种情况出现在linux服务器上(确切的说是使用GCC编译器编译的程序上),特点就是 发生在类函数里,宕机位置不一定,每一次宕机 查看 类体对象内容也是错乱的,并且 关键是 this指针发生了改变,打印其地址时很可能是无法访问,预防方法还是边界访问检查。
结合上一例子来看,段错误宕机重点嫌疑对象就是数组!排查的时候首先在空间近邻或者时间近邻上查找。实际应用中这类错误可能会很隐蔽,例如内存写超的代码发生在某些库代码里,这种情况就要求 写代码时对自己所调用的库函数 要大致了解 实现方法。总之,数组或者数组类指针是我们重点排查对象。
未完待续…… peakflys