Boost Deadline_timer 导致堆栈缓冲区溢出

2024-01-25

最近几天,我一直被 Boost Deadline_timer 的一个非常奇怪的错误困扰。 桌面:Ubuntu 18.04 增强:v1.65.01

当我在类 AddressSanitizer 的构造函数中创建新的 Deadline_timer 时,会捕获来自 Boost 库内部的堆栈缓冲区溢出。

我有一些观察:

  • 我还注意到,如果没有 AddressSanitizer,就会出现问题,因为计时器总是超时,因为 expiry_time 为负数,或者永远不会过期。所以看起来好像有人在某个地方改变了那个内存区域。
  • 我正在使用的类非常大,并且使用相同的 Boost io_service 通过 UDP 发送数据。
  • 我无法仅在独立源文件中重现该错误。
  • 当我删除代码来隔离问题时,无论删除多少代码,问题仍然存在。我已经只创建一个主文件,创建一个io_service和一个deadline_timer,但它仍然会抛出该错误。如果我将其复制到另一个文件中并复制 CMakeLists 条目,我仍然无法重现它。

该类的结构不是很复杂,这里是一个示例类,其本质上是相同的 udp_定时器.hpp

#include "boost/asio.hpp"


class UdpTimer {
    public:
        UdpTimer();
        ~UdpTimer();
        void run();

        void timer_callback(const boost::system::error_code &e);
        void udp_callback(const boost::system::error_code &e, size_t bytes_recvd);
        boost::asio::io_service io;
    
    private:
        boost::asio::ip::udp::socket *socket;
        boost::asio::ip::udp::endpoint *ep;
        boost::asio::deadline_timer *timer;
        char recv_buf[2048];
        unsigned int tot_bytes_recved;
};

udp_timer.cpp

#include "udp_timer.hpp"
#include "boost/bind.hpp"
#include <iostream>

UdpTimer::UdpTimer() {
    // Set up UDP part
  ep = new boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30042);
  socket = new boost::asio::ip::udp::socket(io, *ep);
  socket->async_receive_from(
    boost::asio::buffer(recv_buf, 2048), *ep,
      boost::bind(&UdpTimer::udp_callback, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred)
  );

  tot_bytes_recved = 0;

  timer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(1));
  timer->async_wait(boost::bind(&UdpTimer::timer_callback, this, boost::asio::placeholders::error));
}

UdpTimer::~UdpTimer() {
    delete ep;
    delete socket;
    delete timer;
}

void UdpTimer::run() {
    io.run(); // Never returns
}


// Timer callback. Print info and reset timer
void UdpTimer::timer_callback(const boost::system::error_code &e) {       
    if (e) return;
    static int count = 0;
    std::cout <<"Timer Callback #" <<count++ <<"Bytes received = " <<tot_bytes_recved <<std::endl;
    std::cout <<recv_buf <<std::endl;

    timer->expires_from_now(boost::posix_time::seconds(1));
    timer->async_wait(boost::bind(&UdpTimer::timer_callback, this, boost::asio::placeholders::error));
}

// Udp callback. Update bytes received count
void UdpTimer::udp_callback(const boost::system::error_code &e, size_t bytes_recvd) {
    if (e) return;

    tot_bytes_recved += bytes_recvd;

    socket->async_receive_from(
    boost::asio::buffer(recv_buf, 2048), *ep,
      boost::bind(&UdpTimer::udp_callback, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred)
  );
}


int main(void)  {
    UdpTimer udp_timer;
    udp_timer.run();
}

放置在程序中就足以生成该错误。

=================================================================
==20441==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe4a7621d0 at pc 0x55d73239950c bp 0x7ffe4a761f50 sp 0x7ffe4a761f40
WRITE of size 16 at 0x7ffe4a7621d0 thread T0
    #0 0x55d73239950b in boost::date_time::base_time<boost::posix_time::ptime, boost::date_time::split_timedate_system<boost::posix_time::posix_time_system_config> >::base_time(boost::gregorian::date const&, boost::posix_time::time_duration const&, boost::date_time::dst_flags) (/home/erl/dev/test/build/prog_ins+0x61950b)
    #1 0x55d732396495 in boost::posix_time::ptime::ptime(boost::gregorian::date, boost::posix_time::time_duration) /usr/include/boost/date_time/posix_time/ptime.hpp:40
    #2 0x55d7323d4855 in boost::date_time::microsec_clock<boost::posix_time::ptime>::create_time(tm* (*)(long const*, tm*)) /usr/include/boost/date_time/microsec_time_clock.hpp:116
    #3 0x55d7323d12f6 in boost::date_time::microsec_clock<boost::posix_time::ptime>::universal_time() /usr/include/boost/date_time/microsec_time_clock.hpp:76
    #4 0x55d7323cb501 in boost::asio::time_traits<boost::posix_time::ptime>::now() /usr/include/boost/asio/time_traits.hpp:48
    #5 0x55d7323db197 in boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::expires_from_now(boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::implementation_type&, boost::posix_time::time_duration const&, boost::system::error_code&) (/home/erl/dev/test/build/prog_ins+0x65b197)
    #6 0x55d7323d6a25 in boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >::expires_from_now(boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::implementation_type&, boost::posix_time::time_duration const&, boost::system::error_code&) /usr/include/boost/asio/deadline_timer_service.hpp:129
    #7 0x55d7323d2ca8 in boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>, boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> > >::basic_deadline_timer(boost::asio::io_service&, boost::posix_time::time_duration const&) /usr/include/boost/asio/basic_deadline_timer.hpp:187
    #8 0x55d7323b7f22 in InsHandler::InsHandler(InsConfig*, spdlog::logger*) /home/erl/dev/test/src/InsHandler.cpp:57
    #9 0x55d7323a3fb0 in main /home/erl/dev/test/src/prog_ins.cpp:74
    #10 0x7f369ed89bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #11 0x55d7322894d9 in _start (/home/erl/dev/test/build/prog_ins+0x5094d9)

Address 0x7ffe4a7621d0 is located in stack of thread T0 at offset 480 in frame
    #0 0x55d7323d426f in boost::date_time::microsec_clock<boost::posix_time::ptime>::create_time(tm* (*)(long const*, tm*)) /usr/include/boost/date_time/microsec_time_clock.hpp:80

  This frame has 10 object(s):
    [32, 34) '<unknown>'
    [96, 98) '<unknown>'
    [160, 162) '<unknown>'
    [224, 228) 'd'
    [288, 296) 't'
    [352, 360) 'td'
    [416, 424) '<unknown>'
    [480, 488) '<unknown>' <== Memory access at offset 480 partially overflows this variable
    [544, 560) 'tv'
    [608, 664) 'curr'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/erl/dev/test/build/prog_ins+0x61950b) in boost::date_time::base_time<boost::posix_time::ptime, boost::date_time::split_timedate_system<boost::posix_time::posix_time_system_config> >::base_time(boost::gregorian::date const&, boost::posix_time::time_duration const&, boost::date_time::dst_flags)
Shadow bytes around the buggy address:
  0x1000494e43e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000494e43f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
  0x1000494e4400: f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 f8 f2 f2 f2 f2 f2
  0x1000494e4410: f2 f2 f8 f2 f2 f2 f2 f2 f2 f2 04 f2 f2 f2 f2 f2
  0x1000494e4420: f2 f2 00 f2 f2 f2 f2 f2 f2 f2 00 f2 f2 f2 f2 f2
=>0x1000494e4430: f2 f2 00 f2 f2 f2 f2 f2 f2 f2[00]f2 f2 f2 f2 f2
  0x1000494e4440: f2 f2 00 00 f2 f2 f2 f2 f2 f2 00 00 00 00 00 00
  0x1000494e4450: 00 f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000494e4460: 00 00 00 00 f1 f1 f1 f1 00 f2 f2 f2 f2 f2 f2 f2
  0x1000494e4470: 00 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000494e4480: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f2 f2
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==20441==ABORTING

从这个错误打印输出来看,Boost 库中似乎存在一个错误,该错误将 16 字节写入只分配了 8 字节的内容。但为什么这种情况会断断续续地浮现呢?我还注意到,我们有一些单词被标记为 stack-use-after-scope,即 f8。这是否意味着程序的另一部分在超出范围后使用了指向堆栈分配对象的指针?

使用 valgrind 运行给了我这个

==27251== Conditional jump or move depends on uninitialised value(s)
==27251==    at 0x578FA1: boost::date_time::int_adapter<long>::is_infinity() const (int_adapter.hpp:114)
==27251==    by 0x5772A9: boost::date_time::int_adapter<long>::is_special() const (int_adapter.hpp:131)
==27251==    by 0x5A1069: boost::date_time::counted_time_rep<boost::posix_time::millisec_posix_time_system_config>::is_special() const (time_system_counted.hpp:108)
==27251==    by 0x59FCD3: boost::date_time::counted_time_system<boost::date_time::counted_time_rep<boost::posix_time::millisec_posix_time_system_config> >::add_time_duration(boost::date_time::counted_time_rep<boost::posix_time::millisec_posix_time_system_config> const&, boost::posix_time::time_duration) (time_system_counted.hpp:226)
==27251==    by 0x59EA90: boost::date_time::base_time<boost::posix_time::ptime, boost::date_time::counted_time_system<boost::date_time::counted_time_rep<boost::posix_time::millisec_posix_time_system_config> > >::operator+(boost::posix_time::time_duration const&) const (time.hpp:163)
==27251==    by 0x59E46B: boost::asio::time_traits<boost::posix_time::ptime>::add(boost::posix_time::ptime const&, boost::posix_time::time_duration const&) (time_traits.hpp:57)
==27251==    by 0x5A1BEC: boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::expires_from_now(boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::implementation_type&, boost::posix_time::time_duration const&, boost::system::error_code&) (deadline_timer_service.hpp:161)
==27251==    by 0x5A0811: boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >::expires_from_now(boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::implementation_type&, boost::posix_time::time_duration const&, boost::system::error_code&) (deadline_timer_service.hpp:129)
==27251==    by 0x59F20B: boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>, boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> > >::basic_deadline_timer(boost::asio::io_service&, boost::posix_time::time_duration const&) (basic_deadline_timer.hpp:187)
==27251==    by 0x59DA57: OutputTimer::OutputTimer(boost::asio::io_service*, unsigned int, boost::function<OutputStates ()>) (output_timer.cpp:5)
==27251==    by 0x5877D5: InsHandler::InsHandler(InsConfig*, spdlog::logger*) (InsHandler.cpp:57)
==27251==    by 0x57B149: main (senti_ins.cpp:74)
==27251==  Uninitialised value was created by a stack allocation
==27251==    at 0x59FB3C: boost::date_time::microsec_clock<boost::posix_time::ptime>::create_time(tm* (*)(long const*, tm*)) (microsec_time_clock.hpp:80)

我真的迷失在这里了。我对源代码所做的更改与结果行为之间确实没有任何联系。我可以通过删除完全不相关的头文件的包含来消除错误。但是当包含带有一些函数定义和枚举的mock_header时,错误再次出现。所以当这个错误出现时,它看起来确实是随机的。

对于如何解决此类问题的任何建议,我将非常高兴。

我非常感谢对此的任何建议


更新已编辑的问题

我看到大量动态分配(为什么 C++ 程序员应该尽量减少使用“new”? https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new).

我看到重复的魔术常量(1s,2048),未能以 NUL 终止 recv_buf,然后将其视为 C 字符串,吞噬错误。

删除所有这些:

住在科里鲁 http://coliru.stacked-crooked.com/a/7fb72e7419262739

现场直播 https://wandbox.org/permlink/aDUtXJTpK5TbBB4M

  • udp_timer.h

     //#define BOOST_BIND_NO_PLACEHOLDERS
     #include <boost/asio.hpp>
    
     using boost::asio::ip::udp;
     using namespace std::chrono_literals;
    
     class UdpTimer {
       public:
         UdpTimer();
         void run();
    
       private:
         using error_code = boost::system::error_code;
         void timer_callback(error_code e);
         void udp_callback(error_code e, size_t bytes_recvd);
    
         void do_recv();
         void do_timer();
    
         boost::asio::io_service io;
         udp::endpoint ep { {}, 30042 };
         udp::socket socket { io, ep };
         boost::asio::steady_timer timer { io };
    
         std::array<char, 2048> recv_buf{};
         unsigned int tot_bytes_recved = 0;
     };
    
  • udp_timer.cpp

     #include "udp_timer.hpp"
     using namespace boost::asio::placeholders;
     #include <boost/bind/bind.hpp>
     #include <iostream>
     #include <iomanip>
    
     UdpTimer::UdpTimer() {
         do_recv();
         do_timer();
     }
    
     void UdpTimer::do_recv() {
         socket.async_receive_from(boost::asio::buffer(recv_buf), ep,
             boost::bind(&UdpTimer::udp_callback, this, error, bytes_transferred));
     }
    
     void UdpTimer::do_timer() {
         timer.expires_from_now(1s);
         timer.async_wait(boost::bind(&UdpTimer::timer_callback, this, error));
     }
    
     void UdpTimer::run() {
         io.run(); // Never returns
     }
    
     // Timer callback. Print info and reset timer
     void UdpTimer::timer_callback(error_code e)
     {
         if (e) {
             std::cout << "timer_callback: " << e.message() << std::endl;
             return;
         }
    
         static int count = 0;
         std::cout << "Timer Callback #" << count++
                   << " Bytes received = " << tot_bytes_recved << std::endl
                   << " Last received: " << std::quoted(recv_buf.data()) << std::endl;
    
         do_timer();
     }
    
     // Udp callback. Update bytes received count
     void UdpTimer::udp_callback(error_code e, size_t bytes_recvd) {
         if (e) {
             std::cout << "timer_callback: " << e.message() << std::endl;
             recv_buf[0] = '\0';
             return;
         }
    
         // because you want to print the buffer, you will also want to make sure it
         // is actually NUL terminated
         assert(bytes_recvd < recv_buf.size());
         recv_buf[bytes_recvd] = '\0';
         tot_bytes_recved += bytes_recvd;
    
         do_recv();
     }
    
  • main.cpp

     int main()
     {
         UdpTimer udp_timer;
         udp_timer.run();
     }
    

运行演示,启用 ASAN+UBSAN

旧答案:

boost::asio::io_service io2;
boost::asio::deadline_timer* t = new boost::asio::deadline_timer(io2, boost::posix_time::seconds(1)); 

这仅仅是内存泄漏,但在没有其他代码的情况下,它不可能导致any症状,只是因为不再生成代码:实时编译器资源管理器 https://godbolt.org/z/3Edvrc

现在所有其他观察结果都让你产生怀疑。这是理所当然的!

我无法仅在独立源文件中重现该错误。

这是关键。您的代码中存在未定义的行为。它可能与计时器有关,也可能没有,但肯定不是caused通过这个实例化。

代码的一个明显问题是内存泄漏,以及您首先进行手动分配的事实。这为终身问题打开了大门。

例如。可以想象的是

  • 你在函数中有这些行,io2超出了范围,并且时间对其持有陈旧的引用。

    事实上,这直接对应于“stack-use-after-scope”检测

  • 许多其他场景假设您也t->async_wait()某处

侧面观察是io2意味着您使用两个 io 服务(为什么?)。除此之外,我希望您在实际代码中使用更好的名称,因为很容易迷失在 io2、i、m3、t 等的海洋中:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boost Deadline_timer 导致堆栈缓冲区溢出 的相关文章

随机推荐

  • 显示吸气剂

    我正在研究getters setters 一般的想法是它们是邪恶的 应该避免 您应该让对象完成工作并产生结果 阅读材料 为什么 getter 和 setter 方法是邪恶的 https www javaworld com article 2
  • ASP.NET,如何管理不同类型角色的用户

    我想创建一个包含不同用户的网站 用户可以有不同的角色 管理员和用户 这是一个有据可查的情况 但我还想根据用户的位置对用户进行分组 因此在每个位置我都可以拥有管理员和用户 多维角色系统 原因是 德国用户应该有权访问一组特定的文档 而意大利用户
  • 访问数组内的对象

    I m trying to access values inside Firebase array gt object 当我尝试访问 v for 内部的值时 它运行良好 但我不能这样做 postDetail author 它返回未定义 解决
  • 无法阻止 Visual Studio 2015 在 JS 文件中滞后数秒,提示“JavaScript 语言服务正在后台处理您的请求...”?

    在状态栏中 当我输入 Js 文件时 它会在键盘上显示此消息 并且编辑器挂起 2 或 3 秒 这大概当我将一些 Js 文件移动到类别文件夹中时突然开始发生但我不确定这是原因 我不知道是什么问题 在出现问题之前 所有 js 文件都位于同一文件夹
  • 如何访问 jquery ajax 调用返回的数据? $.ajax(...) 之外;

    我如何访问 jquery ajax 调用返回的数据 在 ajax 之外 Reloads the inital page function jobexist jobname var dataString jobname jobname var
  • Android O Gradle 构建因 travis ci 失败

    我正在尝试将 Travis CI 用于我的 Android 项目 但我的构建不断失败 但可以在本地构建上运行 我正在使用 Android Studio Preview 3 和 gradle 3 alpha 3 我在下面收到此错误 找不到 c
  • 如何删除正在执行的jar文件

    我创建了一个应用程序 在该应用程序中 它根据操作修改 jar 内的属性文件 我可以使用更新的更改创建一个新的临时 jar 但无法将 jar 文件重命名为现有的 jar 文件名 由于它正在运行 我无法删除和重命名它 任何人都可以提出任何操作或
  • data.table 中的条件连接?

    我有以下数据表 dtgrouped2 MonthNo Unique Total 1 1 AAA 10 2 1 BBB 0 3 2 CCC 3 4 2 DDD 0 5 3 AAA 0 6 3 BBB 35 7 4 CCC 15 8 4 AAA
  • Spark Select 与 Scala 列列表

    我正在尝试找到一种使用 List Column 进行火花选择的好方法 我正在爆炸一列 而不是通过爆炸列传回我感兴趣的所有列 var columns getColumns x Returns a List Column tempDf sele
  • 如何在 Seaborn 热图旁边添加列

    鉴于下面的代码会生成热图 我如何获得 D 列 总列 要在热图右侧显示为无颜色的列 只需对齐每个单元格的总值 我还试图将标签移到顶部 我不介意左侧的标签是水平的 因为我的实际数据不会出现这种情况 import matplotlib pyplo
  • len() 是 python 中的函数还是方法?

    在面向对象编程的上下文中 函数不同于方法 当我检查帮助文档时 这段代码 len outputs Signature len obj Docstring Return the number of items in a container Ty
  • 如何将 .gif 转换为 byte[][]?

    我正在使用一个有方法的 API public void setImage byte newImage API 参考中给出的唯一信息是 该图像的格式是简单的原始2D array字节数 每个字节代表灰度black 0 to white 255
  • 在 Windows 上的 Cmake 中链接不同的库以进行调试和发布构建?

    因此 我有一个正在编译的库 我需要链接不同的第三方内容 具体取决于它是调试还是发布版本 特别是这些库的发布或调试版本 在 Cmake 中有一个简单的方法可以做到这一点吗 编辑 我应该注意我正在使用 Visual Studio 根据CMake
  • Debian httpredir 镜像系统在 Docker 中不可靠/不可用?

    简洁版本 Debian 的httpredir debian org镜像服务导致我的 Docker 构建经常失败 因为 apt get 无法下载包或连接到服务器或类似的东西 我是唯一一个有这个问题的人吗 问题是我的 Debian 的还是 Do
  • 是否正在努力开发具有文件自动更改检测功能的面向构建的文件系统?

    我最近开始使用 Git 我发现的有趣功能之一是使用哈希来快速检测更改 另一方面 我看到构建工具 如 make ant javac 等 尝试通过检查文件的时间戳来检测源文件中的更改 这种方法的问题是 如果您从事不止一项工作 机器 你必须确保所
  • 实体框架 4.1 - 选择

    我使用以下表达式 ProductRepository Query Include Function x x ChildProducts Select Function y y PriceTiers Where Function z z Is
  • 将 csv 文件加载到 jQuery 中?

    我有一个 CSV 文件 我想将其用作 jQuery 浮点图的源数据 我是不是该 找到一个可以直接加载 CSV 文件的 jQuery 插件吗 将 CSV 文件转换为 JSON 并使用它 做一些完全不同的事情吗 我没有找到一个可以处理外部 CS
  • 如何在 C++ 中重载运算符 &

    如何在 C 中重载运算符 我试过这个 ifndef OBJECT H define OBJECT H include
  • 当 showdialogwindow 阻止我尝试访问的窗口时,是否有一个事件或我可以使用的东西

    我有 2 个窗户 我们称他们为 A 和 B A 使用 ShowDialog 打开 B 所以我正在打开 B 当用户最小化 B 或以某种方式将其放入后面并尝试再次单击窗口 A 时 它会被阻止 应该如此 但是当发生这种情况时 是否有一个我可以赶上
  • Boost Deadline_timer 导致堆栈缓冲区溢出

    最近几天 我一直被 Boost Deadline timer 的一个非常奇怪的错误困扰 桌面 Ubuntu 18 04 增强 v1 65 01 当我在类 AddressSanitizer 的构造函数中创建新的 Deadline timer