В ядре Linux устранена следующая уязвимость:
nfsd: отозвать идентификаторы состояния копирования-уведомления перед удалением их ссылки
Идентификаторы состояния копирования и уведомления находятся в IDR s2s_cp_stateids и в их родительском элементе.
sc_cp_list stid, закрепленный одной ссылкой на членство.
_free_cpntf_state_locked() отключает запись только после того, как ее количество ссылок достигнет
ноль, поэтому любой путь отзыва, который выполняется во время параллельного
Держатель find_cpntf_state()/manage_cpntf_state() увеличил количество падений cs_count
ссылку, не отсоединяя ее, оставляя запись доступной для обнаружения с ее
ссылка на членство уже использована. Второй отзыв или галочка в прачечной
затем освобождает его, пока читатель все еще удерживает указатель -
KASAN-обнаруживает использование после освобождения в nfs4_put_cpntf_state() считывателя. Это затронуло все три пути отзыва:
- Слив родительского идентификатора (nfs4_free_cpntf_statelist()) неоднократно вызывался
_free_cpntf_state_locked() для первой записи списка; держатель, который имел
увеличенный cs_count заставил его вернуться раньше, поэтому следующая итерация
повторно уменьшил и сжег ссылку держателя.
- OFFLOAD_CANCEL (manage_cpntf_state()) и истечение срока действия прачечной аналогично
использовал _free_cpntf_state_locked() и мог сбрасывать 2->1 без отключения.
Добавьте revoke_cpntf_state_locked(), который дехеширует запись из IDR и
sc_cp_list первым (отдавая окончательную бесплатную версию любому владельцу) и используйте его из
все три пути отзыва. Сток теперь работает с помощью list_for_each_entry_safe().
и отзывает каждую запись безоговорочно, поэтому завершается за один проход за каждый
запись независимо от cs_count. Unhash включен
!list_empty(&cps->cp_list); ворота idr_remove() имеют значение, потому что
idr_alloc_cycl() к тому времени, возможно, уже переработал so_id.
Держите
_free_cpntf_state_locked() только для пути размещения держателя ссылки, где
одновременный отзыв, возможно, уже отменил связь с записью (его list_del_init()
тогда ничего страшного).
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: nfsd: revoke copy-notify stateids before dropping their reference Copy-notify stateids live in the s2s_cp_stateids IDR and on their parent stid's sc_cp_list, pinned by a single membership reference. _free_cpntf_state_locked() only unlinks an entry once its refcount reaches zero, so any revoke path that runs while a concurrent find_cpntf_state()/manage_cpntf_state() holder has elevated cs_count drops the reference without unlinking, leaving the entry discoverable with its membership reference already consumed. A second revoke or a laundromat tick then frees it while the reader still holds the pointer -- a KASAN-detectable use-after-free at the reader's nfs4_put_cpntf_state(). This affected all three revoke paths: - The parent-stid drain (nfs4_free_cpntf_statelist()) repeatedly called _free_cpntf_state_locked() on the first list entry; a holder that had bumped cs_count made it return early, so the next iteration re-decremented and burned the holder's reference. - OFFLOAD_CANCEL (manage_cpntf_state()) and laundromat expiry likewise used _free_cpntf_state_locked() and could drop 2->1 without unlinking. Add revoke_cpntf_state_locked(), which unhashes the entry from the IDR and sc_cp_list first (deferring the final free to any holder), and use it from all three revoke paths. The drain now walks with list_for_each_entry_safe() and revokes each entry unconditionally, so it terminates in one pass per entry regardless of cs_count. The unhash is gated on !list_empty(&cps->cp_list); the idr_remove() gate matters because idr_alloc_cyclic() may have recycled the so_id by then. Keep _free_cpntf_state_locked() for the reference-holder put path only, where a concurrent revoke may already have unlinked the entry (its list_del_init() then a no-op).
Характеристики атаки
Последствия
Строка CVSS v3.1