如何构造C++ future引用局部变量导致崩溃的稳定复现程序
C++ future 悬挂引用问题稳定复现方法
现有代码未崩溃的原因
你当前的测试程序未触发崩溃是未定义行为的典型表现,核心原因有两点:
doThing返回后,local_state所在的栈内存暂时没有被其他函数调用覆盖,残留值仍然是之前的有效值- 部分版本Boost的
future::then默认会inline执行延续任务,如果前序异步任务在doThing返回前就进入完成状态,延续任务甚至可能在local_state生命周期结束前就执行完毕
稳定复现的修改方案
只需要做三处调整就能稳定暴露问题:
- 把内置类型的
local_state替换为带析构逻辑的自定义类型,析构时主动覆写内存,避免残留有效值 - 在
doThing返回后、调用future.get()前,插入栈污染逻辑,覆盖原local_state所在的栈空间 - 延长前序异步任务的等待时间,确保
doThing先完成返回、栈帧完全销毁后,延续任务才开始执行
修改后的测试代码如下:
#include <iostream> #include <cstring> #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #define BOOST_THREAD_PROVIDES_FUTURE #include <boost/chrono.hpp> #include <boost/thread.hpp> #include <boost/thread/future.hpp> // 自定义类型,析构时主动覆写内存 struct State { int val = 0; ~State() { // 覆写为乱码值 val = 0xdeadbeef; } }; void computeResult(State& s) { boost::this_thread::sleep_for(boost::chrono::seconds(2)); std::cout << "computeResult 中读取值: " << s.val << std::endl; s.val += 1; } // 栈污染函数,占用同一块栈空间覆写内存 void pollute_stack() { char buf[1024]; std::memset(buf, 0xcc, sizeof(buf)); // 加局部变量避免编译器优化掉该函数 int volatile tmp = buf[0]; (void)tmp; } boost::future<int> doThing() { State local_state; auto lam = [&]() { return computeResult(local_state); }; auto fut = boost::async(lam); return fut.then([&](auto&&) { return local_state.val; }); // 依然是悬挂引用 } int main() { auto fun = doThing(); // doThing返回后立刻污染栈空间,覆盖原local_state的内存 pollute_stack(); // 等待异步任务和延续任务执行完成 int out = fun.get(); // 正常预期输出是1,出现悬挂引用时会输出0xdeadbeef或者0xcccccccc等乱码,甚至直接崩溃 std::cout << "最终读取值: " << out << std::endl; return 0; }
效果说明
运行修改后的代码,大概率会输出乱码值而非预期的1,部分编译配置下会直接触发段错误,即可稳定复现文章中提到的悬挂引用风险。
内容的提问来源于stack exchange,提问作者fabian
相关产品推荐
相关产品推荐

