Folly拥有一个可用的C++20风格协程库。
在Readme中,它声称:
重要提示:您需要非常小心临时lambda对象的生命周期。调用lambda协程会返回一个捕获对lambda的引用的folly::coro::Task,因此,如果未立即对返回的Task进行co_await,则当临时lambda超出范围时,任务将带有悬挂的引用。
我尝试为提供的示例制作一个MCVE,并对结果感到困惑。 假设所有以下示例都使用以下样板:
#include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/futures/Future.h>
using namespace folly;
using namespace folly::coro;
int main() {
fmt::print("Result: {}\n", blockingWait(foo()));
}
我使用地址灵敏度分析器编译了以下内容,以查看是否会有任何悬空引用。
编辑:澄清问题
问题:为什么第二个示例不触发ASAN警告?
根据cppreference:
当协程到达co_return语句时,它执行以下操作:
...
- 对于co_return expr,其中expr具有非void类型,返回promise.return_value(expr)
- 按相反的顺序销毁所有自动存储期的变量。
- 调用promise.final_suspend()并等待结果。
因此,也许临时lambda的状态实际上要等到返回结果时才被销毁,因为foo
本身是一个协程?
ASAN错误:当协程被等待时,我假设'i'不存在。
auto foo() -> Task<int> {
auto task = [i=1]() -> folly::coro::Task<int> {
co_return i;
}(); // lambda is destroyed after this semicolon
return task;
}
没有错误 -- 为什么?
auto foo() -> Task<int> {
auto task = [i=1]() -> folly::coro::Task<int> {
co_return i;
}();
co_return co_await std::move(task);
}
ASAN错误: 和第一个例子一样的问题吗?
auto foo() -> folly::SemiFuture<int> {
auto task = [i=1]() -> folly::coro::Task<int> {
co_return i;
}();
return std::move(task).semi();
}
无错误...而且为了保险起见,只返回一个常量(没有捕获lambda状态)也可以正常工作。与第一个示例进行比较:
auto foo() -> Task<int> {
auto task = []() -> folly::coro::Task<int> {
co_return 1;
}();
return task;
}
auto task = [i=1]() -> folly::coro::Task<int> { co_await something; co_return i; }
会触发UAF。 - Raymond Chen