上面一種情況是自己內存寫超了,寫到了用戶態別的結構所在的內存,特點就是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