为什么我会"Invalid read of size 8"?(瓦尔格林德)

Why do I get "Invalid read of size 8"? (Valgrind)

本文关键字:林德 size Invalid read 为什么 of      更新时间:2023-10-16

我已经在Stackoverflow上读过类似的帖子,但它们并不能解决我的问题。

我的问题

当我使用 Valgrind 运行可执行文件的调试版本时,我从 Valgrind 收到以下错误消息。

==16631== Invalid read of size 8
==16631==    at 0x217890: std::__detail::__variant::_Uninitialized<long long, true>::_Uninitialized<long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:222)
==16631==    by 0x2178C9: std::__detail::__variant::_Variadic_union<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variadic_union<long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:342)
==16631==    by 0x2178FD: std::__detail::__variant::_Variant_storage<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:399)
==16631==    by 0x2156F0: std::__detail::__variant::_Copy_ctor_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:504)
==16631==    by 0x215716: std::__detail::__variant::_Move_ctor_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:532)
==16631==    by 0x21573C: std::__detail::__variant::_Copy_assign_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:592)
==16631==    by 0x215762: std::__detail::__variant::_Move_assign_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:649)
==16631==    by 0x215791: std::__detail::__variant::_Variant_base<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_base<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:701)
==16631==    by 0x213F29: std::variant<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::variant<0ul, long long const&, long long, void>(std::in_place_index_t<0ul>, long long const&) (variant:1398)
==16631==    by 0x213F6B: std::variant<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::variant<long long const&, void, void, long long, void>(long long const&) (variant:1369)
==16631==    by 0x20B8ED: tgbot::Endpoints::deleteMessage(long long const&, int const&) const (Endpoints.cpp:1957)
==16631==    by 0x2B17F6: ChatCleaner::run(std::shared_ptr<tgbot::Bot> const&) (ChatCleaner.cpp:40)
==16631==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==16631== 
==16631== 
==16631== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==16631==  Access not within mapped region at address 0x0
==16631==    at 0x217890: std::__detail::__variant::_Uninitialized<long long, true>::_Uninitialized<long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:222)
==16631==    by 0x2178C9: std::__detail::__variant::_Variadic_union<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variadic_union<long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:342)
==16631==    by 0x2178FD: std::__detail::__variant::_Variant_storage<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:399)
==16631==    by 0x2156F0: std::__detail::__variant::_Copy_ctor_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:504)
==16631==    by 0x215716: std::__detail::__variant::_Move_ctor_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:532)
==16631==    by 0x21573C: std::__detail::__variant::_Copy_assign_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:592)
==16631==    by 0x215762: std::__detail::__variant::_Move_assign_base<false, long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_storage<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:649)
==16631==    by 0x215791: std::__detail::__variant::_Variant_base<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::_Variant_base<0ul, long long const&>(std::in_place_index_t<0ul>, long long const&) (variant:701)
==16631==    by 0x213F29: std::variant<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::variant<0ul, long long const&, long long, void>(std::in_place_index_t<0ul>, long long const&) (variant:1398)
==16631==    by 0x213F6B: std::variant<long long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<tools::InputFile> >::variant<long long const&, void, void, long long, void>(long long const&) (variant:1369)
==16631==    by 0x20B8ED: tgbot::Endpoints::deleteMessage(long long const&, int const&) const (Endpoints.cpp:1957)
==16631==    by 0x2B17F6: ChatCleaner::run(std::shared_ptr<tgbot::Bot> const&) (ChatCleaner.cpp:40)
==16631==  If you believe this happened as a result of a stack
==16631==  overflow in your program's main thread (unlikely but
==16631==  possible), you can try to increase the size of the
==16631==  main thread stack using the --main-stacksize= flag.
==16631==  The main thread stack size used in this run was 8388608.
==16631== 
==16631== HEAP SUMMARY:
==16631==     in use at exit: 317,149 bytes in 4,488 blocks
==16631==   total heap usage: 1,171,900 allocs, 1,167,412 frees, 359,098,054 bytes allocated
==16631== 
==16631== LEAK SUMMARY:
==16631==    definitely lost: 0 bytes in 0 blocks
==16631==    indirectly lost: 0 bytes in 0 blocks
==16631==      possibly lost: 0 bytes in 0 blocks
==16631==    still reachable: 317,045 bytes in 4,487 blocks
==16631==         suppressed: 104 bytes in 1 blocks
==16631== Reachable blocks (those to which a pointer was found) are not shown.
==16631== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==16631== 
==16631== For lists of detected and suppressed errors, rerun with: -s
==16631== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Speicherzugriffsfehler (Speicherabzug geschrieben)

合适的源代码

"m_msgs_to_delete"是类 ChatCleaner 中的一个静态向量。

32void ChatCleaner::run(const tgbot::Bot::ptr &bot) noexcept
33{
34  if(!m_msgs_to_delete.empty())
35  {
36      //Reverse to have the oldest message at the end.
37      std::reverse(m_msgs_to_delete.begin(), m_msgs_to_delete.end());
38
39      //Delete the message.
40      bot->get_endpnts()->deleteMessage(m_msgs_to_delete.at(m_msgs_to_delete.size() - 1)->chat->id, m_msgs_to_delete.at(m_msgs_to_delete.size() - 1)->message_id);
41      m_msgs_to_delete.pop_back();
42
43      //Reverse again.
44      std::reverse(m_msgs_to_delete.begin(), m_msgs_to_delete.end());
45
46      m_last_time = tools::Tools::get_time();
47      m_msgs_to_delete.shrink_to_fit();
48  }
49}
1953    bool Endpoints::deleteMessage(const long long &chat_id, const int &message_id) const noexcept
{
//HTTP arguments
std::vector<tools::HttpArg> http_args;
1957        http_args.push_back(tools::HttpArg("chat_id", chat_id));
http_args.push_back(tools::HttpArg("message_id", message_id));
tools::HttpClient http_client("https://api.telegram.org/bot" + m_token + "/deleteMessage", http_args);
std::string json = http_client.send_post_req_multipart().m_body;
1963        rapidjson::Document doc;
doc.Parse(json.c_str());
if(doc.IsObject())
if(doc.HasMember("result"))
if(doc["result"].IsBool())
1969                    return doc["result"].GetBool();
else
tools::Tools::write_err_log(Messages::field_does_not_contain_bool("result"));
else
tools::Tools::write_err_log_tmp(Messages::field_non_existent("result"));
else
tools::Tools::write_err_log(Messages::server_resp_not_json_object);
1976
1977        return false;
1978    }

从我所看到的情况来看,错误发生在上面代码段的第 40 行,因为这是文件 ChatCleaner.cpp 的摘录。此行访问一个向量,这完全可能是错误,因为在 Stackoverflow 上遇到类似问题的用户在向量或数组访问方面也遇到了问题。不幸的是,我看不出我做错了什么。此错误错误绝对任意发生,我无法重现它。

可能有帮助的其他说明

  • 该代码来自我的 Telegram 聊天机器人,该机器人 24/7 全天候运行。
  • 错误可能发生在启动软件几分钟后或几天或几周后。
  • 该向量包含已发送消息的 std::shared_ptr<>,这些消息将在一段时间后删除。
  • 我已经检查了矢量内容是否可能被破坏,而发生错误时并非如此。聊天和消息ID完全没问题。我高度假设这只是一个内存访问问题,但我无法弄清楚问题的根源。

问题

问题的原因是什么?

我终于在@cdhowie给出的提示的帮助下找到了问题的根源。消息对象的成员变量"chat"导致了问题,因为正如你在这里看到的

if(doc.HasMember("chat"))
{
if(doc["chat"].IsObject())
chat = std::make_shared<Chat>(tools::Tools::get_json_as_string(doc["chat"]));
else
tools::Tools::write_err_log(Messages::field_does_not_contain_json_obj("chat"));
}

消息构造函数的这一部分仅在传递的 HTTP 响应正文包含正确的字段时创建聊天实例。因此,此成员访问 ChatCleaner 第 40 行中的m_msgs_to_delete.at(m_msgs_to_delete.size() - 1)->chat->id.cpp在这种情况下,堆上不存在空指针,因为"聊天"。

PS:上面的对象文档来自库 rapidjson。定义是rapidjson::Document doc;.其目的是解析 JSON 对象并使数据可访问。整个源代码来自我的电报聊天机器人。