CVE-2026-93221 в Linux Kernel: race condition в nfsd, double free и исправление

CVE: CVE-2026-93221
Продукт: Linux Kernel
Дата публикации: 24.09.2026
Критичность: HIGH
CVSS: 8.1 (3.1)
EPSS: 0,33%; процентиль 23,45%
CISA KEV: нет подтверждения в каталоге CISA KEV

Краткое описание​


Уязвимость CVE-2026-93221 затрагивает модуль nfsd ядра Linux. В структуре struct nfsd_net поля типа bool изменяются из нескольких контекстов без сериализации.

Функция nfsd4_end_grace() проверяет grace_ended как обычное bool, что позволяет двум потокам одновременно пройти проверку и вызвать nfsd4_record_grace_done(). Это приводит к одновременному обходу reclaim_str_hashtbl и двойному освобождению объектов nfs4_client_reclaim.

Основные характеристики​


Риск связан с гонкой данных в серверной части NFSv4, где два независимых контекста ядра одновременно изменяют состояние grace period. Оба вызывающих контекста находятся внутри ядра и активируются штатными механизмами nfsd.

  • Тип ошибки: race condition с double free и повреждением списка.
  • CVSS 3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H, оценка 8.1, severity HIGH.
  • Сложность доступа: высокая из-за необходимости совпадения гонки во времени.
  • Факт эксплуатации: в предоставленных источниках нет подтверждения реальных атак или массового использования.
  • EPSS: 0,33%; процентиль 23,45%.

Какие продукты и версии затронуты​


Затронут компонент nfsd ядра Linux. В предоставленных данных нет списка конкретных версий ядра или дистрибутивов, которые содержат уязвимый код.

  • Компонент: Linux Kernel, подсистема nfsd (NFS Server Daemon).
  • Файлы изменения в исправлении: fs/nfsd/netns.h, fs/nfsd/nfs4proc.c, fs/nfsd/nfs4recover.c, fs/nfsd/nfs4state.c, fs/nfsd/nfsctl.c, fs/nfsd/nfssvc.c.
  • Структура: struct nfsd_net в файле fs/nfsd/netns.h.
  • Функции: nfsd4_end_grace(), nfsd4_record_grace_done(), nfs4_release_reclaim(), nfs4_remove_reclaim_record(), nfsd4_find_reclaim_client().

Причина уязвимости​


В структуре struct nfsd_net несколько boolean-полей изменяются из разных контекстов без сериализации. Ключевое поле grace_ended проверяется и устанавливается как обычное bool в функции nfsd4_end_grace().

Операция if (nn->grace_ended) и запись nn->grace_ended = true не атомарны относительно друг друга. Два контекста могут одновременно прочитать false, записать true и продолжить выполнение.

Оба вызывающих контекста находятся внутри ядра:

  • laundromat path: laundry_wq kworker вызывает nfs4_laundromat(), которая вызывает nfsd4_end_grace().
  • RECLAIM_COMPLETE path: nfsd compound kthread вызывает nfsd4_reclaim_complete(), затем inc_reclaim_complete(), затем nfsd4_end_grace().

После прохождения проверки оба потока вызывают nfsd4_record_grace_done(). Эта функция вызывает активный callback client_tracking_ops->grace_done.

Для legacy_tracking_ops вызывается nfsd4_recdir_purge_old, для cld v1+ вызывается nfsd4_cld_grace_done. Оба пути вызывают nfs4_release_reclaim(), который обходит все бакеты reclaim_str_hashtbl без блокировки и вызывает nfs4_remove_reclaim_record() на каждом элементе.

Функция nfs4_remove_reclaim_record() выполняет list_del и kfree. Два одновременных обхода одного списка приводят к повреждению указателей и двойному освобождению каждого объекта nfs4_client_reclaim.

Третий вызывающий контекст в nfs4_state_start_net() работает под nfsd_mutex до подключения клиентов и до запуска первого delayed work laundromat, поэтому он не участвует в гонке с двумя основными путями.

Как работает атака​


Уязвимость реализуется через временную гонку внутри ядра. Внешний атакующий не управляет потоками напрямую, но может создать условия для активации nfsd и совпадения вызов.

Первый поток входит в nfsd4_end_grace() из laundromat workqueue или RECLAIM_COMPLETE path. Он читает grace_ended как false.

Второй поток одновременно входит в ту же функцию из другого контекста. Он тоже читает grace_ended как false.

Оба потока записывают true и проходят проверку. Оба вызывают nfsd4_record_grace_done().

Функция grace_done вызывает nfs4_release_reclaim(). Она обходит reclaim_str_hashtbl по всем бакетам без блокировки.

Каждый элемент списка обрабатывается через nfs4_remove_reclaim_record(), который удаляет элемент из списка и освобождает память с помощью kfree.

Если два потока одновременно обрабатывают один бакет, указатели в списке повреждаются. Один поток может освободить объект, а второй — использовать уже освобождённую память или повторное освобождение того же объекта.

Функция nfsd4_find_reclaim_client() может одновременно итерировать тот же бакет и читать через освобождённую память.

Условия успешной эксплуатации​


Для реализации ошибки необходимы следующие условия:

  • Активная подсистема nfsd в ядре Linux.
  • Наличие клиентского отслеживания (client tracking) или legacy reclaim path, который использует reclaim_str_hashtbl.
  • Возможность одновременного входа двух контекстов в nfsd4_end_grace(): laundromat kworker и RECLAIM_COMPLETE compound thread.
  • Состояние grace_ended должно быть false в момент чтения обоими потоками.
  • Отсутствие блокировки вокруг проверки и установки grace_ended до исправления.

В предоставленных источниках нет указания на конкретные сетевые настройки или параметры NFS, которые повышают вероятность гонки.

Возможный сценарий атаки​


Сценарий описывает внутреннюю последовательность событий в ядре. Внешний атакующий может пытаться создать нагрузку для nfsd, чтобы увеличить шанс совпадения контекстов.

  • Сервер NFS работает с включённым client tracking или legacy reclaim.
  • Клиент завершает соединение или вызывает RECLAIM_COMPLETE.
  • Laundromat workqueue запускает delayed work и вызывает nfsd4_end_grace().
  • Одновременно compound thread обрабатывает RECLAIM_COMPLETE и вызывает inc_reclaim_complete(), затем nfsd4_end_grace().
  • Оба потока читают grace_ended == false.
  • Оба потока записывают grace_ended = true.
  • Оба потока вызывают nfsd4_record_grace_done().
  • Grace_done callback вызывает nfs4_release_reclaim().
  • Два обхода reclaim_str_hashtbl одновременно удаляют и освобождают элементы списка.
  • Повреждение списка или двойное kfree приводит к некорректному поведению ядра.

Есть ли публичный эксплойт​


В предоставленных источниках нет отдельного PoC, эксплойта или подтверждения публичной эксплуатации CVE-2026-93221.

Исправление опубликовано в stable tree Linux Kernel:


Техническое описание ошибки доступно в описании CVE и в диффах коммитов. Отсутствие сведений об эксплойте не означает, что он невозможен: механика двойного освобождения памяти позволяет корректно воспроизвести гонку при совпадении контекстов.

Признаки эксплуатации​


Специфичных IOC для CVE-2026-93221 в предоставленных источниках нет. Ниже приведены неспецифичные точки контроля, которые можно использовать как общие признаки нестабильности ядра.

  • Падение или зависание процесса nfsd.
  • Ошибки ядра в dmesg с упоминанием NFS, reclaim, grace, list corruption или memory corruption.
  • Аномальные задержки при обработке RECLAIM_COMPLETE или shutdown grace period.
  • Повторяющиеся ошибки в journalctl или syslog после операций с NFSv4 clients.
  • Неожиданное завершение работы nfsd compound threads или laundromat workqueue.

Эти признаки не являются подтверждёнными IOC именно для CVE-2026-93221 и могут возникать по другим причинам.

Как обнаружить атаку​


Для обнаружения потенциальной эксплуатации или нестабильности можно использовать журналы ядра и мониторинг состояния nfsd.

  • Проверка dmesg на ошибки с ключевыми словами: NFS, reclaim, grace, list, memory, double free, use after free.
  • Мониторинг journalctl -k за сообщениями от подсистемы nfsd.
  • Наблюдение за состоянием процессов nfsd и kworker в laundromat queue.
  • Проверка логов NFSv4 clients на ошибки RECLAIM_COMPLETE или grace period.
  • Анализ crash dump при падении ядра на повреждение структур reclaim_str_hashtbl.

Конкретные сигнатуры для автоматического обнаружения CVE-2026-93221 в предоставленных источниках не определены.

Как проверить свою версию​


Для проверки версии ядра Linux можно использовать следующие команды:

Bash:
uname -r

Bash:
uname -a

Bash:
cat /proc/version

Эти команды показывают текущую версию ядра. В предоставленных данных нет списка конкретных версий, которые содержат уязвимость или исправление.

Для проверки наличия исправления в stable tree можно сравнить локальный git-репозиторий с коммитами:

Bash:
git log --oneline | grep 11a5fe42e1811f793e04ef885b639ea7668f439d

Bash:
git log --oneline | grep df5922fe09a8131c793ffa86adf204999b0470f8

Если коммиты присутствуют в истории ядра, исправление применено. Если нет, версия может быть уязвимой.

Исправление​


Исправление состоит из замены разрозненных boolean-полей в struct nfsd_net на единое unsigned long flags word с атомарными операциями.

  • Поле grace_ended заменяется на бит NFSD_NET_GRACE_ENDED в flags.
  • Проверка и установка выполняются через test_and_set_bit(), что гарантирует атомарность на всех архитектурах.
  • Остальные поля (grace_end_forced, in_grace, somebody_reclaimed, track_reclaim_completes, nfsd_net_up, lockd_up) переводятся на test_bit/set_bit/clear_bit.

Исправление доступно в stable tree Linux Kernel:


Рекомендуется обновить ядро до версии, содержащей эти коммиты. В предоставленных источниках нет списка конкретных дистрибутивных пакетов или версий с исправлением.

Временные меры защиты​


В предоставленных источниках нет временных мер защиты.

Общие защитные выводы:

  • Ограничить нагрузку на nfsd, чтобы снизить вероятность совпадения контекстов laundromat и RECLAIM_COMPLETE.
  • Отключить client tracking или legacy reclaim path, если это допустимо для конфигурации NFSv4.
  • Перезапустить nfsd после изменения нагрузки, чтобы сбросить состояние grace period.
  • Мониторинг dmesg на ошибки памяти во время работы nfsd.

Эти меры не устраняют уязвимость и могут повлиять на функциональность NFSv4.

Как проверить устранение уязвимости​


После обновления ядра можно проверить наличие исправления.

  • Проверить версию ядра через uname -r или cat /proc/version.
  • Проверить git-логи локального репозитория ядра на наличие коммитов 11a5fe42e1811f793e04ef885b639ea7668f439d и df5922fe09a8131c793ffa86adf204999b0470f8.
  • Проверить исходный код struct nfsd_net в файле fs/nfsd/netns.h: вместо bool grace_ended должно быть unsigned long flags и enum nfsd_net_flag.
  • Проверить функцию nfsd4_end_grace(): вместо if (nn->grace_ended) должна быть if (test_and_set_bit(NFSD_NET_GRACE_ENDED, &nn->flags)).
  • Провести функциональное тестирование NFSv4 с client tracking и legacy reclaim.

В предоставленных источниках нет автоматического инструмента проверки исправления.

Вывод​


CVE-2026-93221 — это race condition в nfsd ядра Linux, который приводит к двойному освобождению памяти объектов nfs4_client_reclaim. Ошибка возникает из-за неатомарной работы с boolean-полем grace_ended в struct nfsd_net.

Исправление заменяет разрозненные boolean-поля на атомарные операции над unsigned long flags word. Обновление ядра до версии с коммитами 11a5fe42e1811f793e04ef885b639ea7668f439d и df5922fe09a8131c793ffa86adf204999b0470f8 устраняет проблему.

В предоставленных данных нет подтверждения публичной эксплуатации или конкретных версий дистрибутивов. Рекомендация — обновить ядро и проверить наличие исправления через git-логи или исходный код.

Официальные источники​


  1. NVD — CVE-2026-93221
  2. FIRST EPSS — CVE-2026-93221
  3. nfsd: convert nfsd_net boolean flags to unsigned long flags word - kernel/git/stable/linux.git - Linux kernel stable tree
  4. nfsd: convert nfsd_net boolean flags to unsigned long flags word - kernel/git/stable/linux.git - Linux kernel stable tree
  5. CVEs — The Linux Kernel documentation

История обновлений статьи​


  • 26.09.2026 — Опубликована первая версия материала.
 
Назад
Верх Низ