CVE-2026-23294

HIGH CVSS 3.1: 7,0 EPSS 0.02%
Обновлено 2 апреля 2026
Linux
Параметр Значение
CVSS 7,0 (HIGH)
Поставщик Linux
Публичный эксплойт Нет

В ядре Linux устранена следующая уязвимость: bpf: исправлена гонка в devmap на PREEMPT_RT. В ядрах PREEMPT_RT xdp_dev_bulk_queue (bq) для каждого процессора может быть доступ к ним осуществляется одновременно несколькими вытесняемыми задачами на одном и том же ЦП. Исходный код предполагает, что bq_enqueue() и __dev_flush() выполняются атомарно. относительно друг друга на одном и том же процессоре, полагаясь на local_bh_disable() для предотвращения вытеснения.

Однако в PREEMPT_RT local_bh_disable() вызывает миграцию_disable() только (когда PREEMPT_RT_NEEDS_BH_LOCK не установлен) и не отключает вытеснение, которое позволяет планированию CFS вытеснять задачу во время bq_xmit_all(), позволяющий войти другой задаче на том же процессоре bq_enqueue() и одновременно работать с одним и тем же bq для каждого процессора. Это приводит к нескольким гонкам: 1. Двойное освобождение/использование после освобождения на bq->q[]: снимки bq_xmit_all() cnt = bq->count, затем выполняет итерацию bq->q[0..cnt-1] для передачи кадров.

Если вытеснение происходит после моментального снимка, вторая задача может вызвать bq_enqueue(). -> bq_xmit_all() на том же bq, передавая (и освобождая) те же кадры. Когда первая задача возобновляется, она работает на устаревшем указатели в bq->q[], вызывающие использование после освобождения. 2. Повреждение bq->count и bq->q[]: одновременное изменение bq_enqueue() bq->count и bq->q[], пока bq_xmit_all() их читает. 3.

Гонка по удалению dev_rx/xdp_prog: __dev_flush() очищает bq->dev_rx и bq->xdp_prog после bq_xmit_all(). Если вытеснено между bq_xmit_all() возвращает и bq->dev_rx = NULL, вытесняющее действие. bq_enqueue() видит, что dev_rx все еще установлен (не NULL), пропускает добавление bq в flash_list и ставит кадр в очередь. Когда __dev_flush() возобновит работу, он очищает dev_rx и удаляет bq из flash_list, делая осиротевшим недавно поставленный в очередь кадр. 4. __list_del_clearprev() наlush_node: аналогично гонке cpumap, обе задачи могут вызывать __list_del_clearprev() на одном и том жеlush_node, второй разыменовывает указатель prev, уже установленный в NULL.

Гонка между задачей А (__dev_flush -> bq_xmit_all) и задачей Б (bq_enqueue -> bq_xmit_all) на том же процессоре: Задача A (xdp_do_flush) Задача B (перенаправление ndo_xdp_xmit) ---------------------- -------------------------------- __dev_flush(flush_list) bq_xmit_all (бк) cnt = bq->count /* например. 16 */ /* начинаем итерацию bq->q[] */ <-- CFS вытесняет задачу A --> bq_enqueue (dev, xdpf) bq->count == DEV_MAP_BULK_SIZE bq_xmit_all (bq, 0) cnt = bq->count /* те же 16! */ ndo_xdp_xmit(bq->q[]) /* кадры, освобожденные драйвером */ бк->счет = 0 <-- Задача A возобновляется --> ndo_xdp_xmit(bq->q[]) /* use-after-free: кадры уже освобождены! */ Исправьте это, добавив local_lock_t в xdp_dev_bulk_queue и получив это в bq_enqueue() и __dev_flush(). Эти пути уже проходят под local_bh_disable(), поэтому используйте local_lock_nested_bh(), который в не-RT чистая аннотация без накладных расходов, а PREEMPT_RT обеспечивает Спящая блокировка для каждого процессора, которая сериализует доступ к bq.

Показать оригинальное описание (EN)

In the Linux kernel, the following vulnerability has been resolved: bpf: Fix race in devmap on PREEMPT_RT On PREEMPT_RT kernels, the per-CPU xdp_dev_bulk_queue (bq) can be accessed concurrently by multiple preemptible tasks on the same CPU. The original code assumes bq_enqueue() and __dev_flush() run atomically with respect to each other on the same CPU, relying on local_bh_disable() to prevent preemption. However, on PREEMPT_RT, local_bh_disable() only calls migrate_disable() (when PREEMPT_RT_NEEDS_BH_LOCK is not set) and does not disable preemption, which allows CFS scheduling to preempt a task during bq_xmit_all(), enabling another task on the same CPU to enter bq_enqueue() and operate on the same per-CPU bq concurrently. This leads to several races: 1. Double-free / use-after-free on bq->q[]: bq_xmit_all() snapshots cnt = bq->count, then iterates bq->q[0..cnt-1] to transmit frames. If preempted after the snapshot, a second task can call bq_enqueue() -> bq_xmit_all() on the same bq, transmitting (and freeing) the same frames. When the first task resumes, it operates on stale pointers in bq->q[], causing use-after-free. 2. bq->count and bq->q[] corruption: concurrent bq_enqueue() modifying bq->count and bq->q[] while bq_xmit_all() is reading them. 3. dev_rx/xdp_prog teardown race: __dev_flush() clears bq->dev_rx and bq->xdp_prog after bq_xmit_all(). If preempted between bq_xmit_all() return and bq->dev_rx = NULL, a preempting bq_enqueue() sees dev_rx still set (non-NULL), skips adding bq to the flush_list, and enqueues a frame. When __dev_flush() resumes, it clears dev_rx and removes bq from the flush_list, orphaning the newly enqueued frame. 4. __list_del_clearprev() on flush_node: similar to the cpumap race, both tasks can call __list_del_clearprev() on the same flush_node, the second dereferences the prev pointer already set to NULL. The race between task A (__dev_flush -> bq_xmit_all) and task B (bq_enqueue -> bq_xmit_all) on the same CPU: Task A (xdp_do_flush) Task B (ndo_xdp_xmit redirect) ---------------------- -------------------------------- __dev_flush(flush_list) bq_xmit_all(bq) cnt = bq->count /* e.g. 16 */ /* start iterating bq->q[] */ <-- CFS preempts Task A --> bq_enqueue(dev, xdpf) bq->count == DEV_MAP_BULK_SIZE bq_xmit_all(bq, 0) cnt = bq->count /* same 16! */ ndo_xdp_xmit(bq->q[]) /* frames freed by driver */ bq->count = 0 <-- Task A resumes --> ndo_xdp_xmit(bq->q[]) /* use-after-free: frames already freed! */ Fix this by adding a local_lock_t to xdp_dev_bulk_queue and acquiring it in bq_enqueue() and __dev_flush(). These paths already run under local_bh_disable(), so use local_lock_nested_bh() which on non-RT is a pure annotation with no overhead, and on PREEMPT_RT provides a per-CPU sleeping lock that serializes access to the bq.

Характеристики атаки

Способ атаки
Локальный
Нужен локальный доступ
Сложность
Высокая
Сложно эксплуатировать
Нужны права
Низкие
Нужны базовые права
Участие пользователя
Не требуется
Не нужно действие пользователя

Последствия

Конфиденциальность
Высокое
Полная утечка данных
Целостность
Высокое
Полная модификация данных
Доступность
Высокое
Полный отказ в обслуживании

Строка CVSS v3.1