【问题标题】:How to debug stack-overwriting errors with Valgrind?如何使用 Valgrind 调试堆栈覆盖错误?
【发布时间】:2011-04-28 03:57:20
【问题描述】:

我刚刚花了一些时间来寻找一个归结为以下内容的错误。代码错误地覆盖了堆栈,我认为它覆盖了函数调用的返回地址。返回后,程序将崩溃,堆栈将被破坏。在 valgrind 中运行程序会返回如下错误:

vex x86->IR: unhandled instruction bytes: 0xEA 0x3 0x0 0x0
==9222== valgrind: Unrecognised instruction at address 0x4e925a8.

我认为这是因为返回跳转到了一个随机位置,其中包含无效的 x86 操作码。 (虽然我有点怀疑这个地址 0x4e925a8 恰好在一个可执行页面中。我想如果不是这种情况,valgrind 会抛出一个不同的错误。)

我确定问题出在堆栈覆盖类型上,并且我已经修复了它。现在我正在尝试思考如何更有效地捕捉到这样的错误。显然,如果我在堆栈上重写 data,valgrind 不会警告我,但它可能会在有人在堆栈上写入返回地址时捕获。原则上,它可以检测到“推送 EIP”之类的事情何时发生(因此它可以标记返回地址在堆栈上的位置)。

我想知道是否有人知道 Valgrind 或其他任何东西是否可以做到这一点?如果没有,您能否评论有关有效调试此类错误的其他建议。

【问题讨论】:

  • 我建议您针对 valgrind 提交愿望清单错误。
  • 返回地址不是push指令推送的,而是call指令隐式推送的。这样更容易检测到它的位置。
  • @Jan Hudec,这将是 Valgrind 中的一个简洁功能,不是吗?是的,call推送返回地址是正确的,我这样写只是为了完全清楚。

标签: stack-overflow valgrind


【解决方案1】:

一般来说,Valgrind 检测堆栈和全局变量中的溢出是弱到不存在的。可以说,Valgrind 不适合这项工作。

如果您在受支持的平台之一上,使用-fmudflap 构建并使用-lmudflap 链接将为您提供更好的此类错误结果。其他文档here

更新日期:

自从这个答案以来的 6 年里发生了很多变化。在 Linux 上, 查找堆栈(和堆)溢出的工具是 AddressSanitizer,最新版本的 GCC 和 Clang 支持。

【讨论】:

  • 似乎stlinux.com/devel/debug/mudflap 是一个死链接。也许可以使用此链接:gcc.gnu.org/wiki/Mudflap_Pointer_Debugging
  • @bgoodr 我已经更新了答案:与当前最先进的技术相比,挡泥板要弱得多,而且内存开销要大得多。
  • 感谢有关内存开销的提示!
  • AddressSanitizer 的推荐!刚刚找到一个令人讨厌的错误,让我的生活变得超级轻松 :)
  • ASAN 很棒。不幸的是,由于内存开销,它对我不可用。 :(
【解决方案2】:

如果问题发生的足够确定性以至于您可以指出堆栈被破坏的特定函数(在一个可重复的测试用例中),您可以在 gdb 中:

  1. 在进入该函数时中断
  2. 查找返回地址的存储位置(它相对于%ebp(在x86上)(在函数入口处保留%esp的值),我不确定是否有任何偏移量。
  3. 将观察点添加到该地址。您必须使用计算出的数字而不是表达式发出 watch 命令,因为使用表达式 gdb 会在每条指令之后尝试重新评估它,而不是设置陷阱,这会非常慢。
  4. 让函数运行完成。

我还没有使用 gdb7 中提供的 python 支持,但它应该允许自动执行此操作。

【讨论】:

  • 感谢您的回答,除非我学到更好的东西,否则我会接受 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-18
  • 2014-04-06
  • 2012-06-29
  • 2019-09-04
  • 1970-01-01
  • 2011-09-10
  • 1970-01-01
相关资源
最近更新 更多