В ядре Linux устранена следующая уязвимость:
libceph: исправлены два небезопасных голых декодирования в decode_lockers()
decode_lockers() в cls_lock_client.c содержит две операции декодирования
которые позволяют вредоносному или скомпрометированному OSD инициировать выход плиты за пределы поля
читает:
1. ceph_decode_32(p) в поле num_lockers не имеет предшествующих границ
проверьте. ceph_start_decoding() принимает struct_len=0 как допустимую –
внутренний ceph_decode_need(p, end, 0, bad) всегда проходит, поэтому, когда
OSD отправляет struct_len=0, ceph_start_decoding() возвращает успех с
р == конец. Сразу после него голый ceph_decode_32(p) читается
4 байта за пределами подтвержденной границы буфера. Стоимость мусора
передается непосредственно в kzalloc_objs() в качестве счетчика шкафчиков.
Родственная функция decode_watchers() в osd_client.c уже использует
ceph_decode_32_safe() после собственного вызова ceph_start_decoding().
decode_lockers() был единственным сайтом, использующим голый вариант.
2. ceph_decode_8(p) после цикла decode_locker() не имеет предыдущего
проверка границ. Если OSD создает num_lockers так, что цикл
перемещает p точно до конца, последующий голый ceph_decode_8(p) читает
на один байт дальше проверенной границы буфера. Результат передан
непосредственно в *type, который используется в качестве дискриминатора типа блокировки
вызывающим абонентам, предоставляя управляемое OSD однобайтовое чтение OOB с прямым
влияние на поле типа блокировки.
Исправьте оба, заменив простые операции их безопасными вариантами:
ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers,
err_inval)
ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type,
err_free_lockers)
Цели перехода намеренно различаются:
err_inval: новая метка, напрямую возвращающая -EINVAL. Он используется для
путь сбоя предварительного выделения, где *lockers еще не выделено
и не должен передаваться в ceph_free_lockers().
err_free_lockers: существующая метка. Он используется для
путь сбоя после выделения, где *lockers выделены и должны
быть освобожденным.
ret устанавливается в -EINVAL перед ceph_decode_8_safe(), так что
err_free_lockers возвращает правильный код ошибки при нарушении границ.
Без этого err_free_lockers вернул бы устаревшее значение ret (0 из
успешный цикл decode_locker()), молча проглатывая ошибку.
-EINVAL верен для обоих путей отказа. Данные, полученные от
OSD имеет структурную деформацию. -ENOMEM исказит информацию о провале
класс вызывающим абонентам и бэкпортерам Stable@, сортирующим пути ошибок. Модель злоумышленника: вредоносное или скомпрометированное OSD в мультитенантном Ceph
развертывание может вызвать это для любого клиента ядра, который выдает
метод класса lock.get_info (например, во время получения эксклюзивной блокировки RBD).
[идрёмов: обрезать список изменений, форматирование]
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: libceph: fix two unsafe bare decodes in decode_lockers() decode_lockers() in cls_lock_client.c contains two bare decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads: 1. ceph_decode_32(p) at the num_lockers field has no preceding bounds check. ceph_start_decoding() accepts struct_len=0 as valid -- the internal ceph_decode_need(p, end, 0, bad) always passes -- so when an OSD sends struct_len=0, ceph_start_decoding() returns success with p == end. The immediately following bare ceph_decode_32(p) then reads 4 bytes past the validated buffer boundary. The garbage value is passed directly to kzalloc_objs() as the locker count. The sibling function decode_watchers() in osd_client.c already uses ceph_decode_32_safe() after its own ceph_start_decoding() call. decode_lockers() was the only site using the bare variant. 2. ceph_decode_8(p) after the decode_locker() loop has no preceding bounds check. If an OSD crafts num_lockers such that the loop advances p exactly to end, the subsequent bare ceph_decode_8(p) reads one byte past the validated buffer boundary. The result is passed directly into *type, which is used as a lock type discriminator by callers, giving an OSD-controlled one-byte OOB read with direct influence over the lock type field. Fix both by replacing bare operations with their safe variants: ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers, err_inval) ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type, err_free_lockers) The goto targets differ intentionally: err_inval: is a new label returning -EINVAL directly. It is used for the pre-allocation failure path where *lockers is not yet allocated and must not be passed to ceph_free_lockers(). err_free_lockers: is the existing label. It is used for the post-allocation failure path where *lockers is allocated and must be freed. ret is set to -EINVAL before ceph_decode_8_safe() so that err_free_lockers returns the correct error code on bounds violation. Without this, err_free_lockers would return a stale ret value (0 from the successful decode_locker() loop), silently swallowing the error. -EINVAL is correct for both failure paths. The data received from the OSD is structurally malformed. -ENOMEM would misrepresent the failure class to callers and to stable@ backporters triaging error paths. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition). [ idryomov: trim changelog, formatting ]
Характеристики атаки
Последствия
Строка CVSS v3.1
Уязвимые продукты 2
| Конфигурация | От (включительно) | До (исключительно) |
|---|---|---|
|
Linux Linux_Kernel
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
|
4.9
|
7.1.6
|
|
Linux Linux_Kernel
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
|
4.9
|
7.2-rc5
|