Расположение в памяти std :: condition_variable может вызвать ошибку фьютекса - PullRequest
2 голосов
/ 28 мая 2020

У нас была ошибка в нашем программном обеспечении, которая заканчивалась ужасным:

Функция futex вернула неожиданный код ошибки.

Мы отследили ее до проблемы где расположение std :: condition_variable в области памяти mallo c d вызывает ошибку фьютекса. Если std :: condition_variable не выровнен по 16-байтовому слову - тогда это вызывает ошибку фьютекса при попытке wait. В примере первые два вызова wait_for работают, но последний прерывает программу с ошибкой фьютекса.

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);
}

Кажется, я не могу найти документацию по ошибкам фьютекса и почему выравнивание может вызвать это. Кто-нибудь знает, почему это произошло? Это на linux (Arch и Ubuntu) при использовании g cc 9.3.

1 Ответ

2 голосов
/ 28 мая 2020

почему выравнивание может вызвать это

От Черновик C ++ Выравнивание p1 :

Типы объектов имеют требования к выравниванию ([базовые c .fundamental], [basi c .compound]), которые накладывают ограничения на адреса, по которым может быть размещен объект этого типа.

Выражение:

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

вызывает неопределенное поведение в системах, где bad_ptr + 1 не выравнивается с alignof(std::condition_variable). Тестирование на godbolt с gcc10, 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), ядро ​​обнаруживает его здесь и возвращает futex EINVAL. Стандартная библиотека просто закрывает приложение, что является прекрасным поведением для неопределенного поведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...