我們先來看下面的一個例子:
~~~
#include
int main(){
char str[10] = {0};
gets(str);
printf("str: %s\n", str);
return 0;
}
~~~
在 main() 函數内部定義一個字元數組,并通過 gets() 為它指派。
在VS2010 Debug模式下運作程式,當輸入的字元不超過10個時,可以正确輸出,但是當輸入的字元過多時,就會出現運作時錯誤。例如輸入"12345678901234567890",就會出現下面的錯誤:

這是為什麼呢?我們不妨先來看一下 main() 函數的棧:

局部數組也是在棧上配置設定記憶體,當輸入"12345678901234567890" 時,會發生數組溢出,占用“4位元組空白記憶體”、“old ebp”和“傳回位址”所在的記憶體,并将原有的資料覆寫掉,這樣當 main() 函數執行完成後,會取得一個錯誤的傳回位址,該位址上的指令是不确定的,或者根本就沒有指令,是以程式在傳回時出錯。
C語言不會對數組溢出做檢測,這是一個典型的由于數組溢出導緻覆寫了函數傳回位址的例子,我們将這樣的錯誤稱為“棧溢出錯誤”。
> 注意:這裡所說的“棧溢出”是指棧上的某個資料過大,覆寫了其他的資料
局部數組在棧上配置設定記憶體,并且不對數組溢出做檢測,這是導緻棧溢出的根源。除了上面講到的 gets() 函數,strcpy()、scanf() 等能夠向數組寫入資料的函數都有導緻棧溢出的風險。
下面是使用 strcpy() 函數導緻棧溢出的例子:
~~~
#include
#include
int main(){
char *str1 = "sfsdffffffffaeggggggg3r4t4nihfgi23ufhbu4bgui3beugb";
char str2[6] = {0};
strcpy(str2, str1);
printf("str: %s\n", str2);
return 0;
}
~~~
将 str1 複制到 str2,顯然超出了 str2 的接受範圍,會發生溢出,覆寫傳回位址,導緻 main() 函數傳回時出錯。
棧溢出一般不會産生嚴重的後果,但是如果有使用者精心構造棧溢出,讓傳回位址指向惡意代碼,那就比較危險了,這就是常說的棧溢出攻擊