В ядре Linux устранена следующая уязвимость:
ceph: не повторяйте ceph_trim_dentries(), если прогресс невозможен
ceph_cap_reclaim_work() повторно ставит себя в очередь до тех пор, пока
ceph_trim_dentries() возвращает -EAGAIN, что происходит всякий раз, когда аренда
walk исчерпывает свой бюджет nr_to_scan. Это создает цикл занятости, который
потребляет процессор без какого-либо прогресса, когда нечего делать
возврат: без ограничения давления (`count==0`) и каждой сканированной аренды
по-прежнему действителен, при каждом проходе весь бюджет сканирования снижается до нуля и
возвращает `-EAGAIN` только для немедленной постановки в очередь. Прогулка по прямой аренде только усугубила ситуацию.
Когда `expire_dir_lease`
`false` (т. е. мы не собираемся возвращать права аренды),
__dir_lease_check() возвращает `TOUCH` для каждой действительной аренды. `КАСАНИЕ`
перемещает dentry в конец списка и сбрасывает `di->time` через
__dentry_dir_lease_touch(), поэтому просмотр N действительных договоров аренды бессмысленен
переписал список, обновил временные метки (чтобы они никогда не появлялись)
устаревает) и всегда сливал `nr_to_scan`, гарантируя `-EAGAIN`
запрос. Исправьте это в три шага:
- Возвращайте `KEEP` вместо `TOUCH`, когда `expire_dir_lease`
`ложь`. Если мы не собираемся возвращать аренду, оставьте ее в
место вместо перемешивания списка и сброса его временной метки; тот
затем прогулка завершается естественным образом (или через `STOP` при первом же новом
аренда).
- Возвращать `-EAGAIN` только с первого (dentry-lease) обхода, когда что-то
фактически был освобожден.
Полный пакет, который ничего не освобождает, означает повторную попытку. тот же список сразу бесполезен; попасть в договор аренды вместо этого идите. - После обеих прогулок успешно выпрыгните с парашютом (0), когда ничего не освободилось. и давление ограничения отсутствует (`count==0`). Нет никаких оснований продолжайте повторять попытку, когда мы не превысили лимит и не сделали ничего прогресс. При реальном давлении ограничения (`count>0`) путь возврата не меняется и все еще повторяет попытку через `-EAGAIN`.
Без этого патча я видел 500 вызовов ceph_trim_dentries() в секунду на
наши веб-серверы. Это очень заметно в `/proc/lock_stat` (5 минут).
захват):
имя класса con-bounces разногласия время ожидания-мин время ожидания-макс время ожидания-всего время ожидания-среднее время акк-отскоков приобретения время удержания-мин время удержания-макс. время удержания-общее время удержания-среднее
&mdsc->dentry_list_lock: 126180 128218 0,04 8063,44 15986965,20 124,69 1573354 5296812 0,04 8291,28 74164526,48 14,00
-----------------------
&mdsc->dentry_list_lock 111736 [<000000007b11e319>] __ceph_dentry_dir_lease_touch+0x7c/0xa8
&mdsc->dentry_list_lock 2631 [<0000000050597999>] __dentry_leases_walk+0x64/0x2c8
&mdsc->dentry_list_lock 3878 [<00000000c0022f62>] __ceph_dentry_lease_touch+0x5c/0xa8
&mdsc->dentry_list_lock 9973 [<000000002f27cb6f>] __dentry_lease_unlist+0x50/0xa0
-----------------------
&mdsc->dentry_list_lock 123621 [<0000000050597999>] __dentry_leases_walk+0x64/0x2c8
&mdsc->dentry_list_lock 1822 [<000000007b11e319>] __ceph_dentry_dir_lease_touch+0x7c/0xa8
&mdsc->dentry_list_lock 2720 [<000000002f27cb6f>] __dentry_lease_unlist+0x50/0xa0
&mdsc->dentry_list_lock 55 [<00000000c0022f62>] __ceph_dentry_lease_touch+0x5c/0xa8
С этим патчем:
имя класса con-bounces разногласия время ожидания-мин время ожидания-макс время ожидания-всего время ожидания-среднее время акк-отскоков приобретения время удержания-мин время удержания-макс. время удержания-общее время удержания-среднее
&mdsc->dentry_list_lock: 1203 1215 0,16 408,88 33082,88 27,23 4320501 7357389 0,04 500,64 1961578,00 0,27
-----------------------
&mdsc->dentry_list_lock 1029 [<000000003c9aea8a>] __ceph_dentry_dir_lease_touch+0x7c/0xa8
&mdsc->dentry_list_lock 1
---усечено---
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: ceph: do not repeat ceph_trim_dentries() if no progress possible ceph_cap_reclaim_work() re-queues itself for as long as ceph_trim_dentries() returns -EAGAIN, which happens whenever a lease walk exhausts its `nr_to_scan` budget. This creates a busy loop that consumes CPU without making any progress when there is nothing to reclaim: with no cap pressure (`count==0`) and every scanned lease still valid, each pass runs the full scan budget down to zero and returns `-EAGAIN`, only to be queued again immediately. The dir-lease walk made this worse. When `expire_dir_lease` is `false` (i.e. we have no intention of reclaiming dir leases), __dir_lease_check() returned `TOUCH` for every valid lease. `TOUCH` moves the dentry to the tail of the list and resets `di->time` via __dentry_dir_lease_touch(), so a walk over N valid leases pointlessly rewrote the list, refreshed the timestamps (preventing them from ever aging out) and always drained `nr_to_scan`, guaranteeing the `-EAGAIN` requeue. Fix this in three steps: - Return `KEEP` instead of `TOUCH` when `expire_dir_lease` is `false`. If we are not going to reclaim the lease, leave it in place instead of churning the list and resetting its timestamp; the walk then terminates naturally (or via `STOP` at the first fresh lease). - Only return `-EAGAIN` from the first (dentry-lease) walk when something was actually freed. A full batch that frees nothing means retrying the same list immediately is futile; fall through to the dir-lease walk instead. - After both walks, bail out with success (0) when nothing was freed and there is no cap pressure (`count==0`). There is no reason to keep retrying when we are not over the cap limit and made no progress. Under real cap pressure (`count>0`) the reclaim path is unchanged and still retries via `-EAGAIN`. Without this patch, I saw 500 ceph_trim_dentries() calls per second on our web servers. This is very visible in `/proc/lock_stat` (5 minute capture): class name con-bounces contentions waittime-min waittime-max waittime-total waittime-avg acq-bounces acquisitions holdtime-min holdtime-max holdtime-total holdtime-avg &mdsc->dentry_list_lock: 126180 128218 0.04 8063.44 15986965.20 124.69 1573354 5296812 0.04 8291.28 74164526.48 14.00 ----------------------- &mdsc->dentry_list_lock 111736 [<000000007b11e319>] __ceph_dentry_dir_lease_touch+0x7c/0xa8 &mdsc->dentry_list_lock 2631 [<0000000050597999>] __dentry_leases_walk+0x64/0x2c8 &mdsc->dentry_list_lock 3878 [<00000000c0022f62>] __ceph_dentry_lease_touch+0x5c/0xa8 &mdsc->dentry_list_lock 9973 [<000000002f27cb6f>] __dentry_lease_unlist+0x50/0xa0 ----------------------- &mdsc->dentry_list_lock 123621 [<0000000050597999>] __dentry_leases_walk+0x64/0x2c8 &mdsc->dentry_list_lock 1822 [<000000007b11e319>] __ceph_dentry_dir_lease_touch+0x7c/0xa8 &mdsc->dentry_list_lock 2720 [<000000002f27cb6f>] __dentry_lease_unlist+0x50/0xa0 &mdsc->dentry_list_lock 55 [<00000000c0022f62>] __ceph_dentry_lease_touch+0x5c/0xa8 With this patch: class name con-bounces contentions waittime-min waittime-max waittime-total waittime-avg acq-bounces acquisitions holdtime-min holdtime-max holdtime-total holdtime-avg &mdsc->dentry_list_lock: 1203 1215 0.16 408.88 33082.88 27.23 4320501 7357389 0.04 500.64 1961578.00 0.27 ----------------------- &mdsc->dentry_list_lock 1029 [<000000003c9aea8a>] __ceph_dentry_dir_lease_touch+0x7c/0xa8 &mdsc->dentry_list_lock 1 ---truncated---
Характеристики атаки
Последствия
Строка CVSS v3.1