上面一種情況是自己內(nèi)存寫超了,寫到了用戶態(tài)別的結(jié)構(gòu)所在的內(nèi)存,特點(diǎn)就是core文件顯示 別處對象的內(nèi)容亂七八糟,且一般是在對象析構(gòu)時(shí)發(fā)生,這次講解一下,自己把自己寫壞的情況。
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 }
運(yùn)行結(jié)果:
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
段錯(cuò)誤 (core dumped)
堆棧情況:
Program terminated with signal 11, Segmentation fault.
[New process 18836]
#0 0x0000000000400a3e in main ()
(gdb) bt
#0 0x0000000000400a3e in main ()
(gdb)
總結(jié):temp成員函數(shù)在調(diào)用fill成員函數(shù)時(shí)通過thiscall方式調(diào)用,gcc具體實(shí)現(xiàn)是把this指針像普通參數(shù)一樣入棧傳過去,而VS是在定參的情況下,this指針通過ECX傳入,這個(gè)就是這次悲劇的禍根,通過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的持續(xù)寫最終導(dǎo)致棧地址寫到了this指針?biāo)诘臈?nèi)存,這樣再從那塊地址取this指針時(shí),取的就不是this指針,最終從未知的“that指針”取數(shù)據(jù),十有八九程序就掛了。
這種情況出現(xiàn)在linux服務(wù)器上(確切的說是使用GCC編譯器編譯的程序上),特點(diǎn)就是 發(fā)生在類函數(shù)里,宕機(jī)位置不一定,每一次宕機(jī) 查看 類體對象內(nèi)容也是錯(cuò)亂的,并且 關(guān)鍵是 this指針發(fā)生了改變,打印其地址時(shí)很可能是無法訪問,預(yù)防方法還是邊界訪問檢查。
結(jié)合上一例子來看,段錯(cuò)誤宕機(jī)重點(diǎn)嫌疑對象就是數(shù)組!排查的時(shí)候首先在空間近鄰或者時(shí)間近鄰上查找。實(shí)際應(yīng)用中這類錯(cuò)誤可能會很隱蔽,例如內(nèi)存寫超的代碼發(fā)生在某些庫代碼里,這種情況就要求 寫代碼時(shí)對自己所調(diào)用的庫函數(shù) 要大致了解 實(shí)現(xiàn)方法。總之,數(shù)組或者數(shù)組類指針是我們重點(diǎn)排查對象。
未完待續(xù)…… peakflys