首页 > 解决方案 > 了解 C 中的堆栈溢出处理

问题描述

我很好奇如何在 C 中捕获堆栈溢出并偶然发现GNU libsigseg 库

这个库可以在很多平台上捕获堆栈溢出并提供一个实现示例
为了使用该库安装堆栈溢出侦听器,必须首先为备用堆栈保留一些空间。据我了解,这个备用堆栈用于运行侦听器,因为真正的堆栈不可用。

备用堆栈保留在altstack.h(第 40 行)中,如下所示:

[][      ][             ][      ]
|    |           |          |
|    |           |          crumple_zone (8 KiB)
|    |           usable_space (16 KiB)
|    crumple_zone (8 KiB)
offset (31 B)

可用空间是实际使用的空间,而折叠区域的存在是为了防止备用堆栈上的溢出:如果溢出,它会进入分配的空间,防止出现段错误,并且可能有时间检测到它。

但,

  1. 我不明白为什么在堆栈之前和之后会有一个折叠区;堆栈只向一个方向增长。是不是因为某些平台的堆栈朝一个方向增长,而其他平台的堆栈朝另一个方向增长?
  2. 我不明白为什么会有偏移量。

以下是作者给出的解释:

glibc 说:用户应该使用 SIGSTKSZ 作为用户提供的缓冲区的大小。我们希望以一种比崩溃更好的方式检测备用堆栈的堆栈溢出,因此与我们处理 libsigsegv 相比,我们过度分配。此外,我们有意交出一个未对齐的指针,以确保备用堆栈最终仍然对齐。

最后一条语句让我有点不知所措:“......我们故意提交一个未对齐的指针,以确保备用堆栈仍然对齐”。如果我们使堆栈不对齐,堆栈如何最终对齐?

标签: csegmentation-faultstack-overflow

解决方案


我不明白为什么在堆栈之前和之后会有一个折叠区;

堆栈被声明为全局char mystack_storage[...]
假设堆栈向下增长,您需要存储低端的折叠区域来检测备用堆栈本身的溢出。

问题:数组mystack_storage[]后面是什么?
答:你不知道。

如果紧随其后有另一个数组怎么办,如果数组超出范围(例如用类似的东西other_array[-20] = 'a';)怎么办?

要检测这种下溢,您还需要另一端的折叠区。

如果我们使堆栈不对齐,堆栈如何最终对齐?

mystack指针故意错位。如果它直接用作备用堆栈,它将违反许多平台上的堆栈对齐要求,并且很可能会导致崩溃。

为了防止这种情况,库不能mystack直接使用,而是在其他地方对齐。

您指向的代码旨在测试其他代码是否正常工作。

更新:

我仍然不明白偏移量。如果不使用偏移量,为什么 mystack 指针会违反堆栈对齐要求?

没有偏移,mystack的对齐方式是未知的。它可能恰好在 16 字节、8 字节甚至 1 字节边界上对齐。

有了偏移量,就可以保证不对齐(在 1 字节边界上对齐)。

为什么不是相反:通过添加偏移量,它会故意错位并违反堆栈对齐要求。

是的,指针是故意错位的。

关键是:实际使用 mystack的代码(该代码在别处,我没有寻找它)已经准备好mystack通过正确对齐来处理 misaligned (我们称之为“对齐代码”)。您指向的代码旨在练习其他“对齐代码”。

现在,那个“对齐代码”在哪里?

我以为它在图书馆的其他地方,但我错了此处直接使用未对齐的mystack指针。

那么谁在进行所需的对齐呢?内核可以!

来自man sigaltstack

 ss.ss_sp
    This field specifies the starting address of the stack.  When a signal
    handler is invoked on the alternate stack, the kernel automatically
    aligns the address given in ss.ss_sp to a suitable address boundary
    for the underlying hardware architecture.

mystack因此,未对齐旨在执行的“其他代码”不在库中,而是在内核中。


推荐阅读