В настоящее время мы исследуем возможное неопределенное поведение в нашей программе, которое помечено clang7 UBSan в сочетании с boost :: program_option из boost 1.69.0.Мы создали следующий рабочий пример, который мы можем скомпилировать и запустить с clang++ -std=c++17 -fsanitize=undefined -fno-omit-frame-pointer -lboost_program_options debug.cpp && UBSAN_OPTIONS=print_stacktrace=1 ./a.out
#include <iostream>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main() {
std::string test_string = "";
po::options_description desc("test");
desc.add_options()
("string", po::value<std::string>(&test_string))
;
constexpr char *argv[] = {"test", "--string", "test"};
constexpr int argc = sizeof(argv) / sizeof(char*);
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
std::cerr << "Before notify" << std::endl;
po::notify(vm);
std::cout << "string -> " << test_string << std::endl;
}
. Получим следующий вывод:
/usr/include/boost/any.hpp:249:17: runtime error: downcast of address 0x5638e42892e0 which does not point to an object of type 'any::holder<typename remove_cv<basic_string<char, char_traits<char>, allocator<char> > >::type>' (aka 'holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >')
0x5638e42892e0: note: object is of type 'boost::any::holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
00 00 00 00 00 55 f9 63 28 7f 00 00 f8 92 28 e4 38 56 00 00 04 00 00 00 00 00 00 00 74 65 73 74
^~~~~~~~~~~~~~~~~~~~~~~
vptr for 'boost::any::holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
#0 0x5638e1b06d6e in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >* boost::any_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(boost::any*) (/home/gereon/carl/src/tests/carl-settings/a.out+0x4cd6e)
#1 0x5638e1b0672c in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const* boost::any_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(boost::any const*) (/home/gereon/carl/src/tests/carl-settings/a.out+0x4c72c)
#2 0x5638e1b03625 in boost::program_options::typed_value<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>::notify(boost::any const&) const (/home/gereon/carl/src/tests/carl-settings/a.out+0x49625)
#3 0x7f2863f6a71e in boost::program_options::variables_map::notify() (/usr/lib/libboost_program_options.so.1.69.0+0x5771e)
#4 0x5638e1afa2ae in main (/home/gereon/carl/src/tests/carl-settings/a.out+0x402ae)
#5 0x7f2863a13222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)
#6 0x5638e1ad33ad in _start (/home/gereon/carl/src/tests/carl-settings/a.out+0x193ad)
/usr/include/boost/any.hpp:249:114: runtime error: member access within address 0x5638e42892e0 which does not point to an object of type 'any::holder<typename remove_cv<basic_string<char, char_traits<char>, allocator<char> > >::type>' (aka 'holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >')
0x5638e42892e0: note: object is of type 'boost::any::holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
00 00 00 00 00 55 f9 63 28 7f 00 00 f8 92 28 e4 38 56 00 00 04 00 00 00 00 00 00 00 74 65 73 74
^~~~~~~~~~~~~~~~~~~~~~~
vptr for 'boost::any::holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
#0 0x5638e1b06e36 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >* boost::any_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(boost::any*) (/home/gereon/carl/src/tests/carl-settings/a.out+0x4ce36)
#1 0x5638e1b0672c in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const* boost::any_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(boost::any const*) (/home/gereon/carl/src/tests/carl-settings/a.out+0x4c72c)
#2 0x5638e1b03625 in boost::program_options::typed_value<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>::notify(boost::any const&) const (/home/gereon/carl/src/tests/carl-settings/a.out+0x49625)
#3 0x7f2863f6a71e in boost::program_options::variables_map::notify() (/usr/lib/libboost_program_options.so.1.69.0+0x5771e)
#4 0x5638e1afa2ae in main (/home/gereon/carl/src/tests/carl-settings/a.out+0x402ae)
#5 0x7f2863a13222 in __libc_start_main (/usr/lib/libc.so.6+0x24222)
#6 0x5638e1ad33ad in _start (/home/gereon/carl/src/tests/carl-settings/a.out+0x193ad)
string -> test
Как мы видим, он работает нормально (test_string
имеет правильное значение впоследствии).Мы исследовали boost :: program_options и boost :: any и обнаружили следующее: В program_options::typed_value::notify()
(# 2) мы получаем boost :: any и приводим его к типу, который должен быть, вызывая boost :: any_cast().
Учитывая вывод clang (downcast of address 0x5638e42892e0 which does not point to an object of type 'any::holder<typename remove_cv<basic_string<char, char_traits<char>, allocator<char> > >::type>' (aka 'holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >')
и object is of type 'boost::any::holder<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
), мы можем только сделать вывод, что этот boost :: любой экземпляр на самом деле содержит правильный член ... в этом случае downcast должен быть допустимым.
UBSan, однако, утверждает, что никаких ложных срабатываний ожидать не следует.
Интересно, что мы не можем воспроизвести эту ошибку без program_options в миксе:
#include <boost/any.hpp>
#include <iostream>
void any_cast(const boost::any& a) {
const std::string* t2 = boost::any_cast<std::string>(&a);
std::cout << *t2 << std::endl;
}
int main() {
boost::any a;
a = std::string("test");
any_cast(a);
}
Следовательно, мы подозреваем, что (предварительно скомпилированные) program_options могут каким-то образом связываться с типом std :: string.
Кстати, valgrind не сообщает об ошибках обеих программ.
Есть идеи?
Мы пробовали это в Arch linux (с надстройкой 1.69.0-1)., clang 7.0.1-1 с использованием libstd ++) и CentOS 7.6 (clang 7.0.0 с использованием libc ++ и ускорение сборки 1.69.0 с помощью того же clang).