诊断神秘的valgrind"无效写"

Gar*_*han 6 c++ stack-overflow valgrind memory-corruption eglibc

[这是冗长而充满细节的.我的具体问题由下面的粗体字问题引入.]

摘要

我们在valgrind下运行一些测试套件,遇到一个对我来说没什么意义的错误.我正在寻找有关更详细地弄清楚可能出错的建议.

  • Valgrind抱怨"写8号无效".
  • 从运行到运行,错误是一致的,但是应该是不相关的代码更改,不同的编译器/ stdlib版本等.
  • 写入的地址位于堆栈中,据我所知,这是我们编写代码的完全合理的地址.
  • 它的对齐与写入的大小一致.
  • 它发生的地方深入标准库内部.

所有这些都闻起来好像真正的问题在其他地方:某些东西正在变得腐败并导致后来的混乱.但这是valgrind报告的第一个问题,所以如果在其他地方有内存踩踏,那么valgrind就无法抓住它.我怀疑要么我错过了一些明显的东西,要么有一个微妙的问题,那些具有比我更有专业知识的人可能能够指出我.

一些细节

以下是一些细节和一些具体问题.

这是在x64硬件上运行Ubuntu 14.04的Linux机器上.

这是valgrind在一个相当典型的例子中的抱怨:

==14259== Invalid write of size 8
==14259==    at 0x662BBC9: __printf_fp (printf_fp.c:663)
==14259==    by 0x6629792: vfprintf (vfprintf.c:1660)
==14259==    by 0x664D578: vsnprintf (vsnprintf.c:119)
==14259==    by 0x52DCE0F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52E3263: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_float<double>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52E354F: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52EEAF4: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x694725: CRVinfo::appendValue(std::string const&, double) (CRVinfo.cpp:197)
==14259==    by 0x6902DB: CRVdouble::info(CRVinfo&) const (CRVdouble.cpp:103)
==14259==    by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259==    by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259==    by 0x68F87F: CRVvalue::generate() (CRVvalue.cpp:71)
==14259==  Address 0xffeffde68 is on thread 1's stack
==14259==  in frame #0, created by __printf_fp (printf_fp.c:161)
Run Code Online (Sandbox Code Playgroud)

以"CRV"开头的东西是我们的; 他们上面的东西都在libstdc ++和glibc中.Ubuntu 14.04使用glibc的2.19版本 - 除了实际上它似乎使用了eglibc 2.19而不是普通的glibc 2.19; 你可以在这里找到printf_fp.c的相关版本.

运行valgrind --vgdb并向gdb请求反汇编声明(与上面链接的源代码一致),当valgrind阻止我们时,我们实际要执行的指令是callq __mpn_lshift.

涉及"我们的"代码的最顶层堆栈框架如下所示:

void CRVinfo::appendValue(const std::string &name, double value){

  addIndent();
  addElementBegin(name);

  std::ostringstream  oss;
  oss << value;
  m_valueTree.append(oss.str());

  addElementEnd(name);
}
Run Code Online (Sandbox Code Playgroud)

并且内心oss << value;发生了麻烦.m_valueTree是一个std::string; 你能猜到什么样的事情addIndentaddElementBegin做; 后者使用字符串流来做,前者没有.(可能不相关的说明:你可能认为这看起来效率低下而且你是对的,但这根本不是性能关键的代码.)

所以,无论如何,我们在一条callq指令的地址0xffeffde68上得到了大小为8的无效写入.你期望callq写入指向的内存rsp,所以它确实(我已经证实此时rsp等于0xffeffde68)...但是valgrind反对这个,并且我不清楚为什么.

(一个明显的猜测可能是我们的堆栈已经溢出.但是(1)我认为这会发生在看起来更圆的地址上,并且(2)我试图增加堆栈大小而它没有使这些valgrind投诉消失了,(3)我希望堆栈溢出并且没有发生,并且(4)我们此时还没有使用太多堆栈;最早的时候我已经能够探测,rsp是0xfff000598所以我们在故障点使用了不到10k的堆栈.)

问题:对于我来说, valgrind对这个写作有什么反对意见吗?如果没有,有没有办法让valgrind告诉我更多关于它为什么不喜欢它?

问题:这里的直接问题是valgrind中的错误(虽然可能是由我们代码中的一些早期错误行为引起的),这是否合理?如果是这样,有没有什么好的方法来追踪这些事情或排除它们?

问题:这看起来像glibc或libstdc ++的任何已知问题吗?(到目前为止,我所做的这种网络搜索并未发现任何此类已知问题.)

更多信息,以防它有用

如果我允许执行在这个无效写入之后继续执行,那么valgrind会在__mpn_lshift这里调用的函数内部抱怨大小为8 的无效读取.它从同一地址读取并在gdb中反汇编表示不出所料这是retq指令结束__mpn_lshift那是责任.

我的堆栈帧都没有显得非常大.Valgrind并没有抱怨大堆栈帧,询问堆栈是否已经移动,建议增加--max-stacksize,还是任何类型.

在另一台具有略微不同版本的gcc和可能不同版本的标准库的机器上,valgrind再次报告了大小为8的无效写入__printf_fp但在其中的不同部分,这次不在调用指令上.(不幸的是,这是在同事的电脑上,因为我们观察到这一点,所以有一些变化使他的版本显示出与我相同的失败,所以我无法给出任何信心的更多细节.但我是95%确保在mov指令上发生了故障,并严格写入当前堆栈框架内.)

Pau*_*oyd 0

这是一个老问题,但有 7 票赞成。

问题:我应该清楚 valgrind 反对这篇文章的原因吗?如果没有,有什么方法可以让 valgrind 告诉我更多关于它不喜欢它的原因吗?

使用 vgdb 并查看变量的未初始化状态。

具体来说(使用 Valgrind 3.21 及更高版本)你可以做类似的事情

mc xb &value sizeof(value)

那是

mc - memcheck 命令的别名
xb - 检查字节
&value - 检查的地址
sizeof(value) - 长度,在本例中为 8 个字节

问题:这里直接的问题是否可能是 valgrind 中的错误(尽管可能是由我们代码中早期的一些不当行为引起的)?如果是这样,有什么好的方法可以追踪或排除此类情况吗?

并非不可能,但错误更有可能出现在用户代码中。

问题:这看起来像 glibc 或 libstdc++ 的任何已知问题吗?(到目前为止,我所做的网络搜索尚未发现任何此类已知问题。)

不。它看起来像是代码中未初始化的变量。