【问题标题】:Double free when using lambda to function pointer cast使用 lambda 函数指针强制转换时双倍释放
【发布时间】:2019-06-23 21:10:01
【问题描述】:

在一个复杂的程序中花了很多天调试一个非常模糊的错误后,我终于将问题简化为一个非常简单、可重现的场景。

基本上,如果我有一个简单的非捕获 lambda,其签名按值接受单个参数(并按值返回一个对象),然后我将该 lambda 转换为等效的函数指针类型,然后调用函数指针,作为参数传递的对象的析构函数被错误地调用。

这是一个简单的可重现案例,显示了问题,并使用 GCC 4.9.2 导致双重释放:

#include <iostream>
#include <memory>
#include <utility>
#include <cassert>

struct Foo
{
    Foo() = default;
    Foo(const Foo&) = delete;
    Foo& operator = (const Foo&) = delete;

    ~Foo()
    {
        std::cout << "Destroying object " << this << std::endl;
    }
};

auto callback = [](std::unique_ptr<Foo> p)
{
    assert(p);
    return p;
};

int main()
{
    std::unique_ptr<Foo> ptr(new Foo());
    auto fptr = static_cast<std::unique_ptr<Foo>(*)(std::unique_ptr<Foo>)>(callback);
    auto result = fptr(std::move(ptr));
}

该程序的预期行为是分配由unique_ptr 管理的Foo 的唯一实例。然后将该实例作为参数传递给callback 移动一次,然后在callback 按值返回时再次移动该实例,最后销毁该实例。 (当然编译器也可以使用复制省略删除至少一个移动构造函数调用,但这在这里并不重要。)

因此,我真的应该只看到Foo 的析构函数被调用一次。

当我运行这个程序时,我看到了这个:

Destroying object 0xdf4410
Destroying object 0xdf4410
*** Error in `./test9': double free or corruption (fasttop): 0x0000000000df4410 ***

不知何故,编译器生成的代码在传递给callback 时错误地调用了unique_ptr 析构函数。

请注意,如果我将非捕获 lambda 转换为等效的函数指针,会发生这种情况。如果我直接调用callback就没有问题了。

所以我不明白为什么会发生这种情况,因此我想问这是否可能是 lambda 到函数指针转换的编译器错误,或者我只是在这里做错了什么。

【问题讨论】:

  • 无法复制。
  • 我在 Linux 上使用 GCC 4.9.2 并使用 -std=c++14 进行编译
  • 无法用 g++ 8.3.0 和 clang++ 7.0.1 重现;我怀疑 g++ 4.9.2 中有一个错误
  • 也用G++ 5.3.1转载了
  • 无法重现错误,但确实重现了两个 dtor 调用,这对于由 std::unique_ptr 管理的内存来说是不应该发生的。这很可能是一个错误。

标签: c++ function gcc lambda


【解决方案1】:

根据godbolt的测试,直到gcc6.1才修复。 对于由编译器自动生成的函数 lambda::_FUN,2 个不同版本的编译器之间存在明显差异。该函数用于调用 lambda::operator()。

for gcc4.9
lambda::_FUN(unique_ptr<Foo> ptr)
    tmp = ptr //copy constructor is called by compiler, although it's already declared as "delete". it means there are 2 owners for same Foo object.
    lambda::operator(tmp)

for gcc 6.1 or higher
lambda::_FUN(unique_ptr<Foo> ptr)
    lambda::operator(ptr)

【讨论】:

    猜你喜欢
    • 2013-10-31
    • 2012-01-10
    • 2014-03-13
    • 2018-03-05
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    相关资源
    最近更新 更多