了解 C 中的堆栈溢出处理

Posted

技术标签:

【中文标题】了解 C 中的堆栈溢出处理【英文标题】:Understanding stack overflow handling in C 【发布时间】:2021-04-28 03:28:59 【问题描述】:

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

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

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

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

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

但是,

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

以下是作者给出的解释:

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

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

【问题讨论】:

阅读-fsanitize=addresscan catch stack overflows on a lot of platform 我看到库捕获了“页面错误”。不仅仅是***。 @KamilCuk 该库可用于仅捕获堆栈溢出,-fsanitize=address 也具有不可忽略的减速。来源:github.com/google/sanitizers/wiki/… 【参考方案1】:

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

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

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

如果紧随其后有另一个数组怎么办,如果 那个 数组被写入越界(例如,other_array[-20] = 'a'; 之类的东西)怎么办?

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

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

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

为了防止这种情况,库不能直接使用mystack,而是对齐它在别处。

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

更新:

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

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

使用偏移量,保证不对齐(对齐在 1 字节边界上)。

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

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

重点是:实际使用 mystack 的代码(该代码在别处,我没有找到),已经准备好处理未对齐的mystack,通过适当对齐它(让我们称之为“对齐代码”)。您指向的代码旨在行使其他“对齐代码”。

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

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

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

来自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 的错位打算执行的“其他代码”不在库中,而是在内核中。

【讨论】:

而且我一直认为折叠区是耐撞性问题。 感谢您的回答,但我仍然不明白偏移量。为什么mystack 指针在不使用偏移的情况下会违反堆栈对齐要求?为什么不是相反:通过添加偏移量,它会故意错位并违反堆栈对齐要求。 感谢您抽出宝贵时间!我现在明白了。

以上是关于了解 C 中的堆栈溢出处理的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio C/C++ 数组大小未处理的异常堆栈溢出 [重复]

是否可以在 Linux 上预测 C 中的堆栈溢出?

由于堆栈溢出,C 中通常会发生啥?

怎么解决 LINUX 堆栈溢出内存的问题

(原创)攻击方式学习之 - 缓冲区溢出(Buffer Overflow)

.NET 异常处理程序导致 Visual C++ 6.0 异常的堆栈溢出