C栈的奇怪行为

3

我正在使用Ubuntu 14.04和gcc 4.8.2上的Hope函数式程序,尝试递归计算大量质数。但是,我遇到了一个分段错误: 0x000000000040e03f in reach。

cell=<error reading variable: Cannot access memory at address 0x7fffff7feff8> at runtime.c:250

访问地址0x7fffff7feff8时发生分段错误。

reach例程执行的操作是使用垃圾回收标记-清除方法取消标记可以被当前表达式到达的堆项。堆栈非常深(超过100000个调用),但没有发生堆栈溢出:

base_memory = 0x7ffff35a4010
top_string = 0x7ffff35a5260
BaseHeap = 0x7ffff35a5260
heap = 0x7ffff603a450
stack = 0x7ffff72da498
TopStack = 0x7ffff7584d60

从 base_memory 到 TopStack 的区域是使用 malloc 分配的。

每当发生段错误时,地址总是 0x7fffff7feff8,即使是在非常不同的函数中。

如果你谷歌搜索 0x7fffff7feff8,会有相当多的条目显示关于此地址的段错误,但没有解决方案。

我加入了代码来检查堆地址是否在堆范围内,但它从未失败过。

我使用了 gdb 进行调试。

find 0x7ffff35a4010,0x7ffff7584d60,0x7fffff7feff8

什么都没找到。

为什么地址0x7fffff7feff8在许多问题中出现?堆栈机制有问题吗,还是我需要改变代码以适应平台?


3
代码在哪里?(顺便说一下,如果你喜欢找大质数,你需要多出门走走了!) - Ed Heal
我认为这段代码相当复杂。我的问题是,是否存在某种模式或常见错误,因为在谷歌上看到0x7fffff7feff8地址的错误似乎出现了不少次,在使用gdb进行段错误检测时却说不明白。 - DougT
valgrind下运行程序可能会提示问题所在。如果您已经这样做了,请跳过此评论 :) - Diego
我明天可能会尝试使用Valgrind。谢谢。 - DougT
1个回答

4
这似乎是在没有地址空间布局随机化的x86-64系统上的堆栈溢出。如果堆栈从0x7ffffffff000开始,就像在这些系统上一样,0x7fffff7feff8距离堆栈开头下降了8MB,这是Linux系统上常见的默认线程堆栈大小。
转储/proc/self/maps的内容并检查堆栈的开始是否匹配(它在底部列出),然后检查ulimit -s以查看新进程的堆栈大小。如果/proc/self/maps0x7ffffffff000列为堆栈地址范围的末尾,并且ulimit -s打印8192,则您只是遇到了堆栈溢出。在这种情况下,快速修复方法是增加新进程(活动shell的子进程)的堆栈大小,如下所示:
ulimit -Ss size_in_kilobytes

这将在硬限制root可能或可能不会强制执行的情况下工作。从长远来看,重新编写代码以避免过度递归可能是一个好主意。
此外,如果所有这些都与您相关,则应该启用ASLR(sysctl kernel.randomize_va_sapce=1)。如果出现问题,请升级系统。将kernel.randomize_va_space = 1写入/etc/sysctl.conf使其永久生效。

你的分析是正确的。将 kernel.randomize_va_space = 1 写入 /etc/sysctl.conf 对性能的提升最为明显,但更改 ulimit -s 也有所帮助。在函数式编程中,大多数操作都是通过递归完成的。你可以尝试将事物变成尾递归,这样堆栈大小就不会变得太大。现在我很开心。 - DougT
更正:使用ASLR没有任何区别,但是改变ulimit -s会有很大的差异。 - DougT
是的,这很有道理。ASLR 只应该改变栈的位置,而不是其大小,代码将以相同的方式从不同的起点增加它。使用 ASLR,您应该在 /proc/self/maps 中看到不同的堆栈范围,并且堆栈溢出应该发生在不同的地址。 - Wintermute

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接