2010-10-06 50 views
1

有人可以請教我一個常見的例子,藉此(!)你在C程序中銷燬堆棧?我在Ubuntu中使用GCC。 謝謝。在C程序中堆棧破壞的典型例子

+0

你是什麼意思摧毀堆棧? – 2010-10-06 19:04:49

+0

您使用的是哪種CPU架構? Ubuntu有幾個架構端口IIRC。 – 2010-10-06 19:09:58

回答

5

這取決於你所說的「破壞棧」是什麼,但是這是一個常見的錯誤一般會導致重要堆棧數據的損壞:

void dumb() 
{ 
    char small[2]; 
    strcpy(small, "too long to fit"); // Writes past the end of "small", overwriting vital information 
} 

這是安全漏洞的常見來源。它可能被用來劫持指令指針,從而使惡意代碼執行。見buffer overflow

可能被描述爲「破壞棧」的另一個錯誤是infinite recursion的情況下(向下滾動該網頁上):

int add(int n) 
{ 
    return n + add(n + 1); 
} 

其中,因爲它缺乏一個退出條件,將推動這麼多幀到最後得到「滿」的堆棧上。 (除非編譯器可以應用tail-call optimization;請參閱下面的內容)

這兩個例子都沒有像使用GCC 4.4.3的警告那樣編譯。


注:作爲比利奧尼爾下面指出的那樣,這些實施例的行爲是特定於86,而不是C作爲一種語言,並且可以變化從一個編譯器到另一個。這並不是說他們演示瞭如何突破堆在一個特定的(而且非常普遍的)實現C.

+1

注意:所有這些特定於x86。這裏沒有什麼是由C標準規定的。 (C不知道SIGSEGV/EXCEPTION_ACCESS_VIOLATION,也不知道任何類型的虛擬內存模型) – 2010-10-06 19:09:06

+0

是的,只是意識到自己。我會相應地更新我的帖子。感謝您指出了這一點。 – 2010-10-06 19:11:21

+1

@Martin:編輯+1。另一件值得注意的事情是:如果編譯器tailcall優化了'add'函數,它甚至可以在x86上永遠循環而不會崩潰。 – 2010-10-06 19:17:47

0

你不能,至少不符合C標準。你也許可以使用GCC的內聯彙編器功能來處理堆棧指針。

編輯:我想打電話exitabort,或terminate(終止在C++只)會導致棧破壞:P

+0

我真的希望有人會對他們爲什麼低估這個答案給予評論.... – 2010-10-07 02:18:36

+0

我不知道他們爲什麼低估了你的答案,但它肯定對我沒有幫助。我認爲你不瞭解這個問題。 – 2010-10-07 16:46:22

0

我不知道你的意思到底是什麼,但每當你退出一個函數,該函數的「堆棧」被銷燬。例如:

void foo(void) { 
    // a, b and c are "allocated" on the stack here 
    int a, b, c; 

} // a, b and c are destroyed here 

實際上,堆棧永遠不會以您想象的方式被破壞。有一個指向堆棧頂部的指針,並且相對於當前堆棧頂部函數引用位置。當函數退出時,TOS指針減少一定量,但不會發生實際的破壞。所以理論上你仍然可以在函數退出後訪問函數中的值,儘管這是一個壞主意。

你可能想看看這些:

How Does The Function Call Stack Work?

Calling Conventions in C and C++

+0

「TOS」指針?我不知道有這樣的事情的架構。至少在x86上,您要尋找的寄存器是「ESP」。在討論標準中指定的C語言時,這是回答某個特定架構行爲時的問題。 – 2010-10-06 19:14:27

+1

@Billy ONeal:TOS ==堆棧頂部,可能是ESP上的ESP,正如你指出的那樣。 – 2010-10-06 19:21:16

2

這裏有幾個例子,其中堆棧可以得到丟棄。

char* foo() 
{ 
    char str[256]; 
    return str; 
} 

void bar() 
{ 
    char* str = foo(); 
    strcpy(str, "Holy sweet Moses! I blew my stack!!"); 
} 

或者,

void foo() 
{ 
    char* str; // uninitialized; has garbage value 
    strcpy(str, "Holy sweet Moses! I blew my stack!!"); 
    // well, could be anything you are trashing 
} 

void foo() 
{ 
    int* ptr; // uninitialized; has garbage value 
    *ptr = "0xDEADBEEF"; 
    // well, could be anything you are trashing 
} 
+1

好的,第一個例子工作。但第二個例子與堆棧無關。僅僅因爲它是一個堆棧分配指針,並不意味着它與堆棧有任何關係。 – 2010-10-07 02:19:40