【问题标题】:Why does boost::any exhibit undefined behaviour in boost::program_options?为什么 boost::any 在 boost::program_options 中表现出未定义的行为?
【发布时间】:2016-08-17 03:39:01
【问题描述】:

我们直接从boost's documentation举个例子:

#include <iostream>
#include <string>
#include <boost/program_options.hpp>

int main(int const ac, char** const av){

  // Declare the supported options.
  namespace po = boost::program_options;
  using namespace std;
  po::options_description desc("Allowed options");
  desc.add_options()
      ("help", "produce help message")
      ("compression", po::value<int>(), "set compression level")
  ;

  po::variables_map vm;
  po::store(po::parse_command_line(ac, av, desc), vm);
  po::notify(vm);    

  if (vm.count("help")) {
      cout << desc << "\n";
      return 1;
  }

  if (vm.count("compression")) {
      cout << "Compression level was set to " 
   << vm["compression"].as<int>() << ".\n";
  } else {
      cout << "Compression level was not set.\n";
  }
}

程序运行正常。
但是,当使用 gcc 的 sanitizer(或 clang 的)编译时:

g++ -std=c++1z -o main main.cpp -fsanitize=undefined -lboost_program_options

它会产生以下运行时错误:

./main --compression="1"                                                                                                                                                          134
/usr/include/boost/any.hpp:243:16: runtime error: downcast of address 0x000001153fb0 which does not point to an object of type 'holder'
0x000001153fb0: note: object is of type 'boost::any::holder<int>'
 00 00 00 00  20 bc 42 00 00 00 00 00  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  31 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'boost::any::holder<int>'
Compression level was set to 1.

我已将问题提炼为更小的问题:

#include <iostream>
#include <string>
#include <boost/program_options.hpp>

int main(int const argc, char** const argv){

  using namespace boost::program_options;

  //create description
  options_description desc("");

  //add entry
  desc.add_options()
  ("foo",value<std::string>(),"desc");

  //create variable map
  variables_map vm;

  //store variables in map
  positional_options_description pod;
    store(command_line_parser(argc, argv).options(desc).positional(pod).run(), vm);
    notify(vm);

    //get variable out of map
    std::string foo;
    if (vm.count("foo")){
        foo = vm["foo"].as<std::string>(); //UNDEFINED BEHAVIOUR
    }
}

编译:

g++ -std=c++1z -o main main.cpp -fsanitize=undefined -lboost_program_options

执行时:

./main --foo="hello"
/usr/include/boost/any.hpp:243:16: runtime error: downcast of address 0x000000d85fd0 which does not point to an object of type 'holder'
0x000000d85fd0: note: object is of type 'boost::any::holder<std::string>'
 00 00 00 00  b0 c5 5e 90 f8 7f 00 00  98 5f d8 00 00 00 00 00  00 00 00 00 00 00 00 00  31 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'boost::any::holder<std::string>'

很明显,导致 UB 的变量映射的强制转换:

vm["foo"].as<std::string>()  

这正是在线文档显示的方式。

这是误报吗?我的 boost 发行版中是否存在错误?
如果确实安全,我如何避免消毒剂标记它?

【问题讨论】:

  • Boost 的哪个版本? svn.boost.org/trac/boost/ticket/8958
  • @T.C.I 在 1.54.0 上。我更新到1.55.0,问题依旧。我想我会自己应用补丁......
  • 我自己打了补丁,还是没有解决问题。第 257 行已更改为 ? &amp;static_cast&lt;any::holder&lt;BOOST_DEDUCED_TYPENAME remove_cv&lt;ValueType&gt;::type&gt; *&gt;(operand-&gt;content)-&gt;held,但 sanitizer 仍然在第 257 行崩溃。
  • 在 Ubuntu 上,我的包管理器没有显示任何高于 1.55.0 的内容。我想我会尝试从源代码构建。

标签: c++ boost runtime-error boost-program-options any


【解决方案1】:

这似乎是一种未定义的行为。这段代码说明了这个问题:

#include <boost/any.hpp>

int main()
{
    int value = 0;
    int const& const_ref = value;
    boost::any any_var {const_ref};
    boost::any_cast<int&>(any_var); // ubsan error
}

这里 any_var 是用 const 值构造的,并作为非 const int 访问。使用 sanitizer 运行此代码会引发类似于您的运行时错误:

/usr/local/include/boost/any.hpp:259:16: runtime error: downcast of address 0x60200000eff0 which does not point to an object of type 'any::holder<int>'
0x60200000eff0: note: object is of type 'boost::any::holder<int const>'
 01 00 00 0c  b0 ee 49 00 00 00 00 00  00 00 00 00 be be be be  00 00 00 00 00 00 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'boost::any::holder<int const>'
SUMMARY: AddressSanitizer: undefined-behavior /usr/local/include/boost/any.hpp:259:16 in 
/usr/local/include/boost/any.hpp:259:73: runtime error: member access within address 0x60200000eff0 which does not point to an object of type 'any::holder<int>'
0x60200000eff0: note: object is of type 'boost::any::holder<int const>'
 01 00 00 0c  b0 ee 49 00 00 00 00 00  00 00 00 00 be be be be  00 00 00 00 00 00 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'boost::any::holder<int const>'
SUMMARY: AddressSanitizer: undefined-behavior /usr/local/include/boost/any.hpp:259:73 in 

问题是代码中的any_cast&lt;int&amp;&gt; 试图通过向下转换类型擦除指针到any::holder&lt;int&gt; 来访问存储的值,但实际类型是any::holder&lt;int const&gt;。因此,未定义的行为。

boost::program_options 中未定义的行为

在 boost::program_options 中,类型 T 的值作为 any 对象存储在 typed_value&lt;T&gt; 类中。 any 对象的构造如下:

// In class typed_value
typed_value* implicit_value(const T &v)
{
    m_implicit_value = boost::any(v);
    m_implicit_value_as_text =
        boost::lexical_cast<std::string>(v);
    return this;
}

请注意,值 v 被声明为 const 引用。但是,typed_value&lt;T&gt;::notify()(在您的代码中从 po::notify() 调用)在没有 const 的情况下访问存储的值:

template<class T, class charT>
void
typed_value<T, charT>::notify(const boost::any& value_store) const
{
    const T* value = boost::any_cast<T>(&value_store);
    ...
}

这会导致未定义的行为。

解决方法

在 boost/program_options/value_semantic.hpp 中,更改implicit_value()函数的以下行

m_implicit_value = boost::any(v);

m_implicit_value = boost::any(T(v));

这让消毒剂很高兴。我不确定这是否是一个真正的修复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 2012-02-04
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多