CVE-2026-99178 in Linux
Сводка
по VulDB • 28.10.2026
В ядре Linux была устранена следующая уязвимость:
x86/kvm: Не потреблять флаги асинхронной #PF при обработке синхронной #PF в контексте NMI
Событие «страница отсутствует» (page not present) для асинхронного прерывания недействительной страницы (#PF) поступает через вектор #PF, и причина этого события сохраняется во флагах apf_reason.flags на каждом процессоре до тех пор, пока функция __kvm_handle_async_pf() не прочитает их и не сбросит. Если после инъекции события, но до того как обработчик #PF успеет прочитать флаги, поступает прерывание NMI, а сам обработчик NMI вызывает ошибку страницы (#PF), то эта ошибка считывает и сбрасывает флаги, принадлежащие тому #PF, который был прерван. Затем обнаруживается, что в контексте NMI, где произошло прерывание, прерывания отключены, что приводит к панике ядра:
Kernel panic - not syncing: Host injected async #PF in interrupt disabled region Call Trace: <NMI> panic+0x52/0x60 __kvm_handle_async_pf+0xb0/0xb0 exc_page_fault+0x71/0xb0 asm_exc_page_fault+0x27/0x30 RIP: 0010:__get_user_nocheck_8+0x6/0x20 perf_callchain_user+0x147/0x1a0 get_perf_callchain+0x12d/0x1c0 ... perf_event_nmi_handler+0x26/0x50 exc_nmi+0xe8/0x110 RIP: 0010:asm_exc_page_fault+0x0/0x30 </NMI>
Последняя строка с адресом возврата (RIP) показывает, что NMI произошло на первой инструкции входа в обработчик #PF. Утилита perf при сборе пользовательских цепочек вызовов вызывает такую ошибку: во время выборки NMI происходит обход стека пользователя с помощью функции __get_user(), которая завершается ошибкой, если сохраненный указатель кадра не является допустимым адресом в пространстве пользователя или обход выходит за пределы вершины стека. Аналогичное окно возможности (race condition) существует, когда KVM, работающий как гипервизор уровня 1 с параметром DELIVERY_AS_PF_VMEXIT, самостоятельно читает флаги после VM-exit для #PF.
Код входа в NMI сохраняет и восстанавливает регистр CR2 для этой вложенности, но не сохраняет причину асинхронной #PF. Энди Лутамирский (Andy Lutomirski) описал эту гонку условий на примере perf при переработке доставки асинхронных #PF в 2020 году [1]. Сохранение и сброс флагов во время NMI рядом с CR2 обсуждались там, но не были включены в основную ветку.
С тех пор как была проведена эта переработка, Linux прекратил установку флага KVM_ASYNC_PF_SEND_ALWAYS, поэтому хост доставляет событие «страница отсутствует» только для ошибок в пользовательском режиме, и ошибка в режиме ядра в контексте NMI (включая #MC и #DB режима ядра) не может быть асинхронной ошибкой страницы. В этом случае следует вернуться без изменения флагов, чтобы прерванный обработчик нашел их нетронутыми.
Тестирование проводилось на версии v7.3-rc5 в гостевой системе с 4 виртуальными CPU и объемом памяти 2 GiB, восстановленной из снимка состояния (snapshot), память которой обслуживается по требованию через userfaultfd, так что чтение данных вызывает асинхронные #PF, при этом каждый поток выбирает аппаратные циклы с пользовательскими цепочками вызовов и указывает свой указатель кадра на не отображенную страницу. Без этого патча гостевая система падала (panic) в 3 из 3 запусков. С этим патчем гостевая система успешно выдержала все 3 запуска по 60 секунд, а хост зарегистрировал от 148 441 до 153 334 событий kvm_async_pf_not_present в первые 20 секунд каждого запуска. На версии 6.18.50 результаты были аналогичными: гостевая система падала без патча и выживала с ним.
If you want to get best quality of vulnerability data, you may have to visit VulDB.