【问题标题】:Canonical example of stack destruction in a C programC程序中堆栈破坏的典型示例
【发布时间】:2011-04-22 00:24:35
【问题描述】:

有人可以教我一个常见的例子吗(!)你在 C 程序中破坏堆栈?我在 Ubuntu 中使用 GCC。 谢谢。

【问题讨论】:

  • 销毁堆栈是什么意思?
  • 您使用的是什么 CPU 架构? Ubuntu 有几个架构端口 IIRC。

标签: c memory-management stack


【解决方案1】:

你不能,至少,不符合 C 标准。不过,您也许可以使用 GCC 的内联汇编器功能来处理堆栈指针。

编辑:我想调用 exitabortterminate(终止仅在 C++ 中)会导致堆栈破坏:P

【讨论】:

  • 我真的希望有人能就他们为什么不赞成这个答案发表评论......
  • 我不知道他们为什么不赞成您的回答,但这肯定对我没有帮助。我认为你没有理解这个问题。
【解决方案2】:

我不确定您的确切意思,但是每当您退出一个函数时,该函数的“堆栈”就会被破坏。例如:

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++

【讨论】:

  • “TOS”指针?我不知道有这样的东西的架构。至少在 x86 上,您要查找的寄存器是 ESP。这是在谈论标准中规定的 C 语言时,用一种特定架构的行为来回答的问题。
  • @Billy ONeal:TOS == 栈顶,正如你所指出的,可能是 x86 上的 ESP。
【解决方案3】:

这取决于您所说的“销毁堆栈”是什么意思,但这是一个常见错误,通常会导致重要的堆栈驻留数据损坏:

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 编译而没有警告。


注意:正如 Billy ONeal 在下面指出的那样,这些示例的行为特定于 x86,而不是 C 作为一种语言,并且可能因编译器而异。这并不是说他们演示了如何在 C 的特定(并且非常常见的)实现中打破堆栈。

【讨论】:

  • 注意:所有这些都是特定于 x86 的。 C 标准没有规定这里的任何内容。 (C 不知道 SIGSEGV/EXCEPTION_ACCESS_VIOLATION,也不知道任何类型的虚拟内存模型)
  • 是的,我自己也意识到了。我会相应地更新我的帖子。感谢您指出这一点。
  • @Martin:+1 进行编辑。另一件值得注意的事情:如果编译器尾调用优化了 add 函数,它可能会永远循环而不是崩溃,即使在 x86 上也是如此。
  • @Billy ONeal:是的——确认!使用 -O3 编译我的示例时没有崩溃。有趣的。 +1
  • @Billy ONeal:它不能简单地尾调用优化add,尽管它可以用循环替换它;引入累加器参数;和/或发现该函数不可能返回,并将其替换为根本不做任何添加的东西。虽然您说结果特定于 x86,但您也可以说它们特定于 ARM、特定于 MIPS、特定于 PowerPC 等等。很多 C ABI 中都有堆栈功能,如果有堆栈,这些程序往往会破坏它......
【解决方案4】:

这里还有一些堆栈可能会被丢弃的示例。

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
}

【讨论】:

  • 好的,第一个例子有效。但第二个例子与堆栈无关。仅仅因为它是一个堆栈分配的指针并不意味着它与堆栈有任何关系。
猜你喜欢
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 2011-11-01
  • 1970-01-01
相关资源
最近更新 更多