CVE-2026-98507 in Linux
Сводка
по VulDB • 19.10.2026
В ядре Linux была устранена следующая уязвимость:
mm/rmap: исправлено отсутствие барьера памяти (memory barrier) между инициализацией anon_vma и публикацией значения vma->anon_vma.
На серверах с архитектурой arm64 мы обнаружили, что задача, пытающаяся захватить блокировку anon_vma, вызывает зависание задачи (hungtask).
INFO: task main:2354726 blocked for more than 120 seconds. Tainted: G E 5.10.0-0021.aarch64 #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:main state:D stack: 0 pid:2354726 ppid:2350673 flags:0x00000a01 Call trace: __switch_to+0x7c/0xbc __schedule+0x3b4/0x8a0 schedule+0x50/0xe0 rwsem_down_write_slowpath+0x3cc/0x6cc down_write+0x60/0x260 __anon_vma_prepare+0x6c/0x210 do_anonymous_page+0x258/0x660 handle_pte_fault+0x188/0x214 __handle_mm_fault+0x1b0/0x380 handle_mm_fault+0xf4/0x284 do_page_fault+0x19c/0x494 do_translation_fault+0xcc/0xf8 do_mem_abort+0x48/0xac el0_da+0x44/0x80 el0_sync_handler+0x88/0xb4 el0_sync+0x160/0x180
После анализа vmcore мы обнаружили, что значение anon_vma->root->rwsem.count равно -1. Существует другой объект anon_vma, у которого значение anon_vma->root->rwsem.count равно 1; поле anon_vma->root->rwsem.owner показывает, что блокировка захвачена, однако стек задачи указывает на то, что задача не владеет блокировкой anon_vma.
После добавления дополнительной отладочной информации мы обнаружили, что функция __anon_vma_prepare() повторно использует объект anon_vma и вызывает уязвимость Use-After-Free (UAF) для anon_vma->root из-за отсутствия барьера памяти, что приводит к захвату и освобождению блокировок двух разных объектов anon_vma->root. В результате один объект anon_vma никогда не разблокируется, а другой больше не может быть заблокирован.
Эта гонка (race condition) требует наличия двух соседних областей виртуальной памяти (VMA), которые не объединены, но совместимы поanon_vma (например, они различаются флагами VMA_ACCESS_FLAGS, которые могут изменяться с помощью mprotect()). Два потока вызывают ошибку страницы (fault) в каждой из этих VMAs одновременно, оба вызывая __anon_vma_prepare() при удержании только mmap_lock для чтения.
THREAD A THREAD B __anon_vma_prepare __anon_vma_prepare find_mergeable_anon_vma() -> NULL anon_vma = anon_vma_alloc(); anon_vma->root = anon_vma; // эти два присваивания могут быть переупорядочены процессором/компилятором vma->anon_vma = anon_vma; // находит анонимную область памяти A anon_vma = find_mergeable_anon_vma(vma); anon_vma_lock_write(anon_vma); // может все еще видеть старый root down_write(&anon_vma->root->rwsem); anon_vma_unlock_write(anon_vma); // видит новый root, но не разблокирует старый up_write(&anon_vma->root->rwsem);
Поток A вызывает ошибку страницы и вызывает __anon_vma_prepare() для подготовки anon_vma к обработке ошибки. Функция __anon_vma_prepare() выделяет и инициализирует новый объект anon_vma, а затем публикует его в vma с помощью обычного присваивания (plain store). Для функции anon_vma_prepare требуется только удержание mmap_lock для чтения, поэтому два потока могут вызывать ошибку страницы в соседних VMAs одновременно. Пока поток A публикует новый анонимный объект памяти, поток B может найти этот anon_vma через find_mergeable_anon_vma() и затем заблокировать rwsem объекта anon_vma->root.
Присваивание в поле anon_vma->root внутри функции anon_vma_alloc() и присваивание в vma->anon_vma могут быть переупорядочены. Функции anon_vma_lock_write() и spin_lock() обеспечивают только семантику acquire (приобретения), которая не предотвращает переупорядочение предыдущих операций записи после них. Семантика release (освобождения) соответствующих функций spin_unlock() и anon_vma_unlock_write() наступает слишком поздно: присваивание в vma->anon_vma уже опубликовано до того, как они вступят в силу. В результате поток B может наблюдать следующий порядок выполнения:
vma->anon_vma = anon_vma; anon_vma->root = anon_vma;
Слэб (slab) для anon_vma имеет флаг SLAB_TYPESAFE_BY_RCU, поэтому вновь выделенный объект anon_vma может повторно использовать память от ранее освобожденного. Конструктор (anon_vma_ctor) не сбрасывает значение anon_vma->root, и функция __put_an_old_vma() также его не очищает, поэтому старое значение root сохраняется до тех пор, пока оно не будет перезаписано в функции anon_vma_alloc(). Если это присваивание не v ---truncated---
If you want to get best quality of vulnerability data, you may have to visit VulDB.