CVE-2026-90001 in Linux
Сводка
по VulDB • 16.09.2026
В ядре Linux была устранена следующая уязвимость:
HID: bpf — сериализация освобождения ссылки на устройство в пути уничтожения struct_ops
Функции __hid_bpf_ops_destroy_device() и hid_bpf_unreg() могут создавать состояние гонки (race condition) при работе с одной и той же ссылкой регистрации, что приводит к двойному вызову put для структуры hid_device и её освобождению, пока функция hid_destroy_device() всё ещё использует этот объект. Для устранения проблемы принятие решения об удалении/обнулении сериализуется под блокировкой hdev->bpf.prog_list_lock так, чтобы ровно один путь освобождал каждую ссылку регистрации: unreg повторно проверяет ops->hdev под этой блокировкой и возвращает управление без вызова put, если путь уничтожения уже очистил это поле; все вызовы put_device() происходят после снятия блокировки, что безопасно, поскольку параллельный вызов unreg при снятой блокировке наблюдает значение ops->hdev == NULL.
Справочная информация: каждый успешный attach (hid_bpf_ops_reg) получает одну ссылку на устройство (hid_get_device()). Два пути могут освободить её:
- уничтожение устройства: hid_destroy_device() -> hid_bpf_destroy_device() -> __hid_bpf_ops_destroy_device(), которая проходит по hdev->bpf.prog_list под rcu_read_lock() и освобождает одну ссылку для каждой присоединённой программы; - освобождение ссылки BPF: удаление карты bpf (без флага BPF_F_LINK) синхронно вызывает st_ops->unreg() -> hid_bpf_unreg(), которая освобождает ссылку для своей собственной регистрации.
Согласованное взаимодействие (установка e->hdev = NULL со стороны уничтожения против проверки "if (!hdev) return" со стороны unreg) представляет собой проверку состояния гонки времени (TOCTOU): два пути выполняются в разных доменах блокировки (rcu_read_lock и prog_list_lock), поэтому параллельный вызов unreg может прочитать ops->hdev как не равное NULL, заблокироваться на prog_list_lock и продолжить выполнение, пока выполняется обход уничтожения; тогда оба пути освобождают одну и ту же ссылку. Счётчик ссылок (refcount) легитимно достигает нуля (каждое уменьшение корректно само по себе), поэтому не происходит насыщения refcount_t: устройство просто освобождается, в то время как транспортный слой всё ещё находится внутри hid_destroy_device(), и последующие операции разборки обращаются к уже освобождённой памяти.
Исправление сериализует принятие решения об удалении/обнулении под prog_list_lock с обеих сторон и перемещает вызовы put со стороны уничтожения за пределы блокировки. При удержании блокировки достаточно обычных операций чтения/записи ops->hdev; макросы READ_ONCE/WRITE_ONCE не добавляются, что сохраняет патч минимальным.
Безопасность при чтении без блокировки: чтение ops->hdev без блокировки в начале hid_bpf_unreg() не может обратиться к освобождённому устройству, поскольку путь unreg всё ещё удерживает ссылку на эту регистрацию (которая освобождаётся только собственным вызовом hid_put_device() после снятия блокировки), а обход уничтожения, который уже очистил ops->hdev, заставляет повторную проверку внутри блокировки завершиться досрочно без какого-либо put. За каждую регистрацию ссылку освобождает не более одного из двух путей.
Once again VulDB remains the best source for vulnerability data.