Патч уже в 6.19 и пойдет в бэкпорты, чтобы прерывания выключались правильно во всех сценариях.

В ядре Linux снова нашли баг, который годами сидел в одном из самых чувствительных мест системы: обработке page fault на x86. Это тот самый момент, когда процессор внезапно понимает, что программа полезла в память, к которой сейчас нет корректного доступа, и ядро должно быстро и аккуратно разобраться, что делать дальше. И вот выяснилось, что с 2020 года в этой логике была тонкая, но принципиальная ошибка: прерывания отключались не всегда так, как предполагалось.
Исправление уже приняли в ветку Linux 6.19, а дальше его планируют портировать и в более старые стабильные серии. Инициатором стал инженер Intel Седрик Синь (Cedric Xing), который раскопал проблему в коде обработки исключения page fault и предложил простой, но более надежный подход.
Суть истории упирается в старый комментарий внутри функции do_page_fault() для x86. Там много лет объяснялось, что при обработке ошибок доступа к памяти прерывания могли быть повторно включены, особенно в сценариях с ошибками на пользовательских адресах. При этом прямо признавалось, что пройтись по всем возможным веткам выхода и гарантировать правильное состояние прерываний в каждом месте почти нереально: получится либо «адский» комбинаторный ремонт, либо переворот логики с ног на голову.
Но, как выяснилось, этот комментарий был немного неправильным, а вместе с ним неправильной была и логика вокруг него. Проблема не ограничивалась «ошибками на пользовательских адресах». В обработчике смешались две разные идеи: диапазон адреса (пользовательский или ядра) и контекст, в котором был выполнен доступ (условно, пользовательский сценарий или ядровой). Эти вещи связаны интуитивно, но на практике не равны. Бывают ситуации, когда обращение к адресам ядра происходит в пользовательском контексте, и тогда отдельные ветки обработки способны включить прерывания, хотя на выходе они должны быть снова выключены, прежде чем управление вернется в низкоуровневый обработчик исключения.
Один из примеров, который всплыл при разборе, связан с веткой __bad_area_nosemaphore(): она действительно пытается восстановить «правильное» состояние, включая и затем отключая прерывания, но делает это не везде и не всегда одинаково. В результате получалась асимметрия: в зависимости от пути выполнения прерывания могли остаться включенными там, где ожидалось обратное.
Инженеры пришли к выводу, что пытаться аккуратно «подлатать» все разветвления не имеет смысла. Вместо этого сделали то, что обычно и считают самым безопасным в подобных местах: отключать прерывания повторно и безусловно в одном конкретном месте, перед возвратом к низкоуровневой части обработки page fault. Ради этого убрали неполный частный случай из кода, который пытался решить проблему выборочно, и заменили его на простое правило: не важно, какой адрес вызвал fault, перед выходом нужно гарантированно привести состояние прерываний к ожидаемому.
Любопытно, что корни проблемы тянутся к изменениям, попавшим в ядро еще во время merge window Linux 5.8 в 2020 году. Теперь ошибка закрыта в актуальной ветке, а затем, судя по планам, доберется и до поддерживаемых стабильных выпусков. Для пользователей это не про «ускорение» или заметную новую функцию, а про более корректное и предсказуемое поведение ядра в редких, но потенциально опасных ситуациях, где цена мелкой асимметрии может быть высокой.