В ядре Linux устранена следующая уязвимость:
nfsd: удерживать rcu через повторную попытку localio cmpxchg
Объекты nfsd_file освобождаются через call_rcu (filecache.c:296) и
nfsd_file_slab создается без SLAB_TYPESAFE_BY_RCU
(KMEM_CACHE(nfsd_file, 0) в filecache.c:789), поэтому полосовая страница
резервное копирование освобожденного файла nfsd_file становится доступным для свободного восстановления после того, как RCU
истекает льготный период. Блок снова: повтор в nfsd_open_local_fh() загружает указатель с
cmpxchg, а затем вызывает nfsd_file_get(new) (что
refcount_inc_not_zero) без удержания rcu_read_lock. Единственный звонивший
nfs_open_local_fh() удаляет rcu_read_lock перед вызовом этого помощника,
поэтому никакая внешняя критическая секция на стороне чтения не покрывает нагрузку.
ЦП 0 (nfsd_open_local_fh) ЦП 1 (nfsd_file_put_local)
----- -----
новый = cmpxchg(pnf, NULL, ...)
nf = xchg(pnf, NULL)
nfsd_file_put (нф)
последняя ссылка -> call_rcu()
/* истекает льготный период;
страница плиты переработана */
nfsd_file_get (новый)
refcount_inc_not_zero(&new->nf_ref)
/* работает с переработанной памятью */
Ненулевое слово по смещению nf_ref переработанного объекта делает
кажется, что удар по счетчику ссылок прошел успешно, и вызывающая сторона затем разыменовывает
new->nf_net и new->nf_file из освобожденной памяти. Исправьте это, выполнив rcu_read_lock() непосредственно перед cmpxchg и
отпуская его на всех трех выходах блока if (нового): goto-again
повторите попытку, путь очистки потерянной гонки и путь успешной установки.
nfsd_file_put() и nfsd_net_put() остаются за пределами раздела RCU, поэтому
они по-прежнему могут блокировать.
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: nfsd: hold rcu across localio cmpxchg retry nfsd_file objects are freed via call_rcu (filecache.c:296), and nfsd_file_slab is created without SLAB_TYPESAFE_BY_RCU (KMEM_CACHE(nfsd_file, 0) at filecache.c:789), so the slab page backing a freed nfsd_file becomes freely reclaimable once the RCU grace period elapses. The again: retry block in nfsd_open_local_fh() loads a pointer with cmpxchg and then calls nfsd_file_get(new) (which is refcount_inc_not_zero) without holding rcu_read_lock. The sole caller nfs_open_local_fh() drops rcu_read_lock before invoking this helper, so no outer reader-side critical section covers the load. CPU 0 (nfsd_open_local_fh) CPU 1 (nfsd_file_put_local) ----- ----- new = cmpxchg(pnf, NULL, ...) nf = xchg(pnf, NULL) nfsd_file_put(nf) last ref -> call_rcu() /* grace period elapses; slab page recycled */ nfsd_file_get(new) refcount_inc_not_zero(&new->nf_ref) /* operates on recycled memory */ A non-zero word at the nf_ref offset of the recycled object makes the refcount bump appear to succeed, and the caller then dereferences new->nf_net and new->nf_file out of freed memory. Fix by taking rcu_read_lock() immediately before the cmpxchg and releasing it on all three exits of the if (new) block: the goto-again retry, the lost-race cleanup path, and the install-succeeded path. nfsd_file_put() and nfsd_net_put() stay outside the RCU section so they remain free to block.
Характеристики атаки
Последствия
Строка CVSS v3.1