Boost::spirit::hold_any内存损坏

boost::spirit::hold_any memory corruption

本文关键字:内存 损坏 any hold spirit Boost      更新时间:2023-10-16

我有一个大的代码库,可以使用boost::any或boost::spirit::hold_any(取决于宏定义)。

hold_any似乎与boost::any兼容(例如,如何将boost::any打印到流?或类型擦除-第四部分)和更快(为什么你不应该使用boost::any),但我正在使用hold_any (boost v1.55/1.54/1.53)遇到几个分段错误。

这是一个最小的工作示例,显示了与原始代码相同的问题:
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/home/support/detail/hold_any.hpp>
typedef boost::spirit::hold_any any;
typedef std::vector<any> vany;
int main()
{
  vany data0, data1;
  for (unsigned i(0); i < 1000; ++i)
  {
    std::string s("test_test_test");
    data0.push_back(any(s));
  }
  const unsigned n(data0.size());
  vany::iterator iter(data0.begin());
  for (unsigned i(0); i < n; ++i)
  {
    std::cout << "Moving " << i << std::endl;
    data1.push_back(*iter);
    iter = data0.erase(iter);
  }
  return 0;
}

程序运行正常:

  • boost::spirit::hold_anyboost::any;
  • hold_any的内容更改为足够小的数据类型以执行小缓冲区优化(例如从std::stringint)。

在Boost Spirit这样一个被广泛使用的库中可能会有一些重大错误,这似乎很奇怪,但是

  • 我很难在例子中找到一个bug;
  • 我试过g++/clang++,但是没有成功。

这个例子有什么问题?

你应该使用hold_any,因为它是在detail/hold_any.hpp的原因。

也就是说,hold_any的复制分配似乎被破坏了。我已经创建了在github上的拉请求与建议的修复。

没有修复,下面的程序演示了UB(因为编译器生成了一个浅赋值操作符,这是首选):

#include <iostream>
#include <string>
#include <boost/spirit/home/support/detail/hold_any.hpp>
typedef boost::spirit::hold_any any;
int main()
{
    any b;
    {
        any a;
        a = std::string("test_test_test");
        b = a;
    }
    std::cout << "b: " << b << 'n';
}

在valgrind下运行时:

==11827== Invalid read of size 8
==11827==    at 0x5E9D793: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std
==11827==    by 0x4012FC: boost::spirit::detail::fxns<mpl_::bool_<true> >::type<std::string, char>::stream_out(std::ostream&, void* const*) (hold_any.hpp:113)
==11827==    by 0x4010F5: std::basic_ostream<char, std::char_traits<char> >& boost::spirit::operator<< <char>(std::basic_ostream<char, std::char_traits<char> >&, boost::spirit::basic_hold_any<char> const&) (hold_any.hpp:368)
==11827==    by 0x400FC9: main (test.cpp:17)
==11827==  Address 0x8ac1650 is 0 bytes inside a block of size 39 free'd
==11827==    at 0x4C2BADC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827==    by 0x5EC405E: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.18)
==11827==    by 0x401204: boost::spirit::detail::fxns<mpl_::bool_<true> >::type<std::string, char>::static_delete(void**) (hold_any.hpp:89)
==11827==    by 0x401328: boost::spirit::basic_hold_any<char>::~basic_hold_any() (hold_any.hpp:246)
==11827==    by 0x4010B4: boost::spirit::basic_hold_any<char>::~basic_hold_any() (hold_any.hpp:245)
==11827==    by 0x400FA0: main (test.cpp:15)