You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GCC Address Sanitizer报heap-use-after-free:代码错误还是误报?

boost::asio::io_context通过unique_ptr移动后触发Address Sanitizer堆内存非法访问问题

问题场景

因boost::asio::io_context无法直接移动,使用std::unique_ptr管理其生命周期。启用-fsanitize=address编译运行时,触发heap-use-after-free告警,但移动前后unique_ptr指向的io_context地址完全一致,且异步定时器的回调能正常执行,推测告警与定时器销毁有关(使用GCC 11.4.0)。

测试代码

#include <boost/asio.hpp>
#include <memory>
#include <iostream>

int main()
{
    std::unique_ptr<boost::asio::io_context> io_ptr{
        std::make_unique<boost::asio::io_context>()
    };

    boost::asio::steady_timer timer{*io_ptr, std::chrono::seconds{1}};
    std::cout << "io_ptr address: " << io_ptr.get() << std::endl;
// 本地输出示例: io_ptr address: 0x602000000010

    timer.async_wait([](const boost::system::error_code&) {
        std::cout << "Timer called\n"; // 能正常输出
    });

    std::unique_ptr<boost::asio::io_context> io_ptr2{std::move(io_ptr)};
    std::cout << "io_ptr2 address: " << io_ptr2.get() << std::endl;
// 地址与之前一致: io_ptr2 address: 0x602000000010
    io_ptr2->run_one();
    std::cout << "Done\n"; // 能正常输出

    return 0;
}

问题原因

这不是Address Sanitizer的误报,而是变量销毁顺序导致的内存非法访问:

  • 栈上的timer对象创建早于持有io_context的io_ptr2,根据C++栈变量的销毁规则,main函数结束时会先销毁io_ptr2,进而释放其管理的堆内存(即io_context对象)。
  • 随后销毁栈上的timer时,其析构函数会尝试与关联的io_context交互(比如清理未完成的异步操作状态),但此时io_context已经被销毁,触发heap-use-after-free告警。

解决方案

调整变量的生命周期,确保timer在io_context被销毁前完成析构:

  • 将timer放入局部作用域,让其在io_context销毁前先被释放:
#include <boost/asio.hpp>
#include <memory>
#include <iostream>

int main()
{
    std::unique_ptr<boost::asio::io_context> io_ptr{
        std::make_unique<boost::asio::io_context>()
    };

    std::cout << "io_ptr address: " << io_ptr.get() << std::endl;

    {
        boost::asio::steady_timer timer{*io_ptr, std::chrono::seconds{1}};
        timer.async_wait([](const boost::system::error_code&) {
            std::cout << "Timer called\n";
        });

        std::unique_ptr<boost::asio::io_context> io_ptr2{std::move(io_ptr)};
        std::cout << "io_ptr2 address: " << io_ptr2.get() << std::endl;
        io_ptr2->run_one();
    } // timer在此处销毁,此时io_context仍存在

    std::cout << "Done\n";

    return 0;
}
  • 也可以调整变量声明顺序,让io_ptr2的声明早于timer,这样timer会先被销毁,再销毁io_ptr2。

内容的提问来源于stack exchange,提问作者SupAl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:42:45