std :: condition_variable的内存位置可能导致futex错误

问题描述 投票:2回答:1

我们在软件中存在一个以可怕的代码结束的错误:

futex工具返回了意外的错误代码。

我们将其追溯到一个问题,其中std :: condition_variable在内存分配区域内的位置会导致futex错误。如果std :: condition_variable未与16字节字对齐-当您尝试wait时,它将导致futex错误。在该示例中,前两个wait_for调用起作用,但最后一个调用因futex错误而中止程序。

void futex_error()
{
    /* init */
    std::mutex mtx;

    /* Normal one works  */
    std::cout << "Doing normal" << "\n";
    std::condition_variable* con_var = (std::condition_variable*)malloc(sizeof(std::condition_variable));
    new (con_var) std::condition_variable{};

    {
        std::unique_lock<std::mutex> lck(mtx);
        con_var->wait_for(lck, std::chrono::seconds(1));
    }

    /* Clean */
    con_var->std::condition_variable::~condition_variable();
    free(con_var);

    std::cout << "Doing 16 bytes" << "\n";
    /* Works on 16 byte alignment  */
    uint8_t* ptr_16 = (uint8_t*)malloc(sizeof(std::condition_variable) + 16);
    std::condition_variable* con_var_16 = new (ptr_16 + 16) std::condition_variable{};

    {
        std::unique_lock<std::mutex> lck(mtx);
        con_var_16->wait_for(lck, std::chrono::seconds(1));
    }

    /* Clean */
    con_var_16->std::condition_variable::~condition_variable();
    free(ptr_16);

    std::cout << "Doing 1 byte" << "\n";
    /* Futex error */
    uint8_t* bad_ptr = (uint8_t*)malloc(sizeof(std::condition_variable) + 1);
    std::condition_variable* bad = new (bad_ptr + 1) std::condition_variable{};

    {
        std::unique_lock<std::mutex> lck(mtx);
        bad->wait_for(lck, std::chrono::seconds(1)); //<--- error here?
    }

    /* Clean */
    bad->std::condition_variable::~condition_variable();
    free(con_var);
}

我似乎找不到有关futex错误以及为什么对齐会导致此错误的文档。有谁知道为什么会这样?这是在Linux(Arch和Ubuntu)上,同时使用gcc 9.3。

c++ memory-management memory-alignment
1个回答
2
投票

为什么对齐会导致此问题

来自C++ draft Alignment p1

对象类型具有对齐要求([basic.fundamental],[basic.compound]),这些条件对可以分配该类型的对象的地址施加了限制。

表达式:

new (bad_ptr + 1) std::condition_variable{};

bad_ptr + 1alignof(std::condition_variable)不对齐的系统上调用未定义的行为。使用gcc10在godbolt上进行测试,alignof(std::confition_variable)等于8

bad->两者都是未对齐的访问,并且都是未定义的行为。

有人知道为什么会这样吗?

在执行可执行文件时检查strace输出,我们可以看到:

futex(0x557da3e262e9, FUTEX_WAIT_BITSET_PRIVATE, 0, {tv_sec=2439, tv_nsec=619296657}, FUTEX_BITSET_MATCH_ANY) = -1 EINVAL (Invalid argument)

因为uaddr应作为指向int调用的futex的指针的第一个参数未与_Alignof(int)对齐,所以内核将其检测为here,并且futex返回了EINVAL。然后,标准库将退出应用程序,对于未定义的行为,这是一个非常好的行为。

© www.soinside.com 2019 - 2024. All rights reserved.