В ядре Linux устранена следующая уязвимость:
ceph: исправлен UAF в check_new_map() в сеансе, освобожденном во время разблокировки
check_new_map() перебирает mdsc->sessions[] и для каждого активного сеанса
удаляет mdsc->mutex для выполнения операций за сеанс. Принудительное закрытие
путь (ранг удален с карты) правильно принимает ссылку на s через
ceph_get_mds_session() перед освобождением mdsc->mutex, но три других
пути не имеют:
Путь A (адрес изменен): mutex_unlock → mutex_lock(&s->s_mutex)
Путь B (переподключение): mutex_unlock → send_mds_reconnect(mdsc, s)
Путь C (активный переход): mutex_unlock → mutex_lock(&s->s_mutex)
Без дополнительной ссылки другой поток может получить mdsc->mutex.
во время окна разблокировки вызовите __unregister_session(), который удаляет
последнюю ссылку на s и освободите ее. Затем исходный поток обращается к
освободил память через s->s_mutex.
Исправьте, добавив ceph_get_mds_session(s) перед каждым mutex_unlock и
ceph_put_mds_session(s) после соответствующего mutex_lock, совпадающего
шаблон, уже использованный в пути принудительного закрытия. Хронология гонки (Путь А):
Тема A (check_new_map) Тема B (еще одно обновление карты)
содержит mdsc->mutex или разрыв сеанса)
-------------------------- --------------------------
s = mdsc->сессии[i]
(refcount == 1, удерживается только
массив сессий[])
mutex_unlock(&mdsc->мьютекс)
---> приобретает mdsc->мьютекс
__unregister_session(mdsc, с)
сеансы[i] = NULL
ceph_put_mds_session(s)
рефсчет: 1 -> 0
kfree(s) <--- освобожден!
mutex_lock(&s->s_mutex)
UAF на освобожденном s->s_mutex
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: ceph: fix UAF in check_new_map() on session freed during unlock check_new_map() iterates mdsc->sessions[] and for each active session drops mdsc->mutex to perform per-session operations. The forced-close path (rank removed from map) correctly takes a reference on s via ceph_get_mds_session() before releasing mdsc->mutex, but three other paths do not: Path A (address changed): mutex_unlock → mutex_lock(&s->s_mutex) Path B (reconnect): mutex_unlock → send_mds_reconnect(mdsc, s) Path C (active transition): mutex_unlock → mutex_lock(&s->s_mutex) Without the extra reference, another thread can acquire mdsc->mutex during the unlock window, call __unregister_session() which drops the last reference on s, and free it. The original thread then accesses freed memory via s->s_mutex. Fix by adding ceph_get_mds_session(s) before each mutex_unlock and ceph_put_mds_session(s) after the corresponding mutex_lock, matching the pattern already used in the forced-close path. Race timeline (Path A): Thread A (check_new_map) Thread B (another map update holds mdsc->mutex or session teardown) -------------------------- -------------------------- s = mdsc->sessions[i] (refcount == 1, held only by sessions[] array) mutex_unlock(&mdsc->mutex) ---> acquires mdsc->mutex __unregister_session(mdsc, s) sessions[i] = NULL ceph_put_mds_session(s) refcount: 1 -> 0 kfree(s) <--- freed! mutex_lock(&s->s_mutex) UAF on freed s->s_mutex
Характеристики атаки
Последствия
Строка CVSS v3.1