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

协程接收聚合参数时出现Double-free错误:Bug还是未定义行为?

GCC 11-12中协程代码的Double-free问题分析

问题场景

以下C++协程代码在最新版GCC和Clang中运行正常,但在GCC 11至GCC 12版本中会触发Double-free错误,汇编代码中可观察到对std::string的重复析构调用。

复现代码

#include <coroutine>
#include <iostream>
#include <string>

struct Coro{
    struct Promise;
    struct Awaiter{
        bool await_ready(){return true;}
        void await_resume(){}
        void await_suspend(std::coroutine_handle<Promise> coro){}
    };
    struct Promise{
        Coro get_return_object(){return {};}
        auto initial_suspend() noexcept{return std::suspend_never{};}
        auto final_suspend() noexcept{return std::suspend_never{};}
        void unhandled_exception(){}
        Awaiter await_transform(const Coro&){
            return {};
        }
        void return_void(){}
    };
    using promise_type = Promise;
};

struct Aggregate{
    //(*)
    //Aggregate(const std::string& s):s{s}{}
    std::string s;
};

static_assert(std::is_aggregate_v<Aggregate>);

Coro f1(Aggregate){
    co_return;
}

Coro f2(std::string){
    co_return;
}

Coro g(){
    std::string foo{"foo"};
    // 1. 触发Double-free的写法
    co_await f1(Aggregate{"/"+foo});
    // 2. 正常的写法(取消注释调用f2)
    //co_await f2("/" + foo);
    // 3. 正常的写法(取消注释拆分调用与co_await)
    //Coro c = f1(Aggregate{"/"+foo});
    //co_await c;
}

int main(){
    g();
}

问题分析

现象验证

在GCC 12.3生成的汇编代码中,可清晰看到对std::string的两次析构调用,即Double-free:

call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
        jmp     .L168
        mov     rbx, rax
.L168:
        mov     rax, QWORD PTR [rbp-40]
        add     rax, 40
        mov     rdi, rax
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]
        mov     rax, rbx

结论:这是GCC编译器Bug

该问题属于GCC 11-12版本的编译器Bug,并非C++标准定义的未定义行为。原因是编译器在处理协程中co_await表达式内的聚合初始化临时对象时,错误地重复插入了析构调用,导致临时std::string被两次释放。

相关Bug报告

该问题对应GCC Bugzilla中的核心Bug条目:

  • Bug 105547:协程中传递聚合类型临时参数时触发重复析构
  • Bug 106243:优化阶段错误处理协程内临时对象的销毁时机

这些Bug已在GCC 13及后续版本中修复,因此最新版GCC不再出现该问题。

临时解决方案

以下任一修改可消除Double-free错误:

  • 在Aggregate中添加用户定义构造函数(取消注释标记(*)的代码),使其不再是聚合类型
  • 直接传递std::string给函数(取消注释代码中2.部分,调用f2)
  • 将f1的调用与co_await拆分到不同行(取消注释代码中3.部分)

注:将f1的参数改为引用传递无法解决该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:15:12