CVE-2026-52969
Beschrijving NL
In de Linux kernel is de volgende kwetsbaarheid verholpen:
KVM: Verwerpen gewikkelde offset in kvm_reset_dirty_gfn()
kvm_reset_dirty_gfn () bewaakt het gfn-bereik met
if (!memslot || (offset + __fls(masker)) >= memslot- > npagina's)
return;
maar offset is u64 en de toevoeging is niet aangevinkt. De controle kan worden
geruisloos omzeild door een u64 wrap. De vuile ring achter die vermeldingen is MAP_SHARED OP
KVM_DIRTY_LOG_PAGE_OFFSET van de vcpu fd, zodat de VMM de
sleuf- en offsetvelden van elke invoer tussen wanneer de kernel duwt
ze en wanneer KVM_RESET_DIRTY_RINGS ze verbruikt. Bij het resetten,
kvm_dirty_ring_reset() leest de waarden opnieuw via READ_ONCE() en feeds
ze direct terug in deze cheque; alleen de vlaggen handdruk is
behandeld als de overdracht, wordt de slot/offset payload in vertrouwen genomen. Twee inzendingen maken
ingang[i].offset = 0xffffffffffffffc1
invoer[i+1].offset = 0
maakt de coalescentielus in kvm_dirty_ring_reset() compute
delta = (s64)(0 - 0xffffffffffffc1) = 63
die in [0, BITS_PER_LONG) valt, dus het vouwt ingang[i+1] in de
bestaand masker door bit 63 in te stellen. De achterliggende kvm_reset_dirty_gfn()
call ziet dan offset = 0xffffffffffffc1 en __fls(masker) = 63;
de som is 0 in u64 en de grenscontrole passeert. Die offset verspreidt zich naar kvm_arch_mmu_enable_log_dirty_pt_masked()
ongewijzigd. Op het oude MMU-pad -- kvm_memslots_have_rmaps() ==
true, d.w.z. schaduwpaginering, elke VM die schaduwwortels heeft toegewezen, of
een schrijf-gevolgd slot -- het bereikt gfn_to_rmap(), die indexeert
slot- >arch.rmap[0][] met een near-U64_MAX gfn. Dat is een
out-of-bounds belasting van een kvm_rmap_head, gevolgd door een voorwaardelijke
vrij van PT_WRITABLE_MASK in waar de geladen aanwijzer ook naar wijst. Het pad is bereikbaar vanuit elk proces dat /dev/kvm bevat. Bereik-controle offset op zijn eigen eerste, zodat de toevoeging kan niet wikkelen. memslot->npages is begrensd ver onder U64_MAX, dus een keer offset <
npages hold, offset + __fls(masker) (met __fls(masker) < BITS_PER_LONG)
binnen bereik blijft.
Origineel (Engels) tonen
In the Linux kernel, the following vulnerability has been resolved:
KVM: Reject wrapped offset in kvm_reset_dirty_gfn()
kvm_reset_dirty_gfn() guards the gfn range with
if (!memslot || (offset + __fls(mask)) >= memslot->npages)
return;
but offset is u64 and the addition is unchecked. The check can be
silently bypassed by a u64 wrap.
The dirty ring backing those entries is MAP_SHARED at
KVM_DIRTY_LOG_PAGE_OFFSET of the vcpu fd, so the VMM can rewrite the
slot and offset fields of any entry between when the kernel pushes
them and when KVM_RESET_DIRTY_RINGS consumes them. On reset,
kvm_dirty_ring_reset() re-reads the values via READ_ONCE() and feeds
them straight back into this check; only the flags handshake is
treated as the handover, the slot/offset payload is taken on trust.
Crafting two entries
entry[i].offset = 0xffffffffffffffc1
entry[i+1].offset = 0
makes the coalescing loop in kvm_dirty_ring_reset() compute
delta = (s64)(0 - 0xffffffffffffffc1) = 63
which falls in [0, BITS_PER_LONG), so it folds entry[i+1] into the
existing mask by setting bit 63. The trailing kvm_reset_dirty_gfn()
call then sees offset = 0xffffffffffffffc1 and __fls(mask) = 63;
the sum is 0 in u64 and the bounds check passes.
That offset propagates into kvm_arch_mmu_enable_log_dirty_pt_masked()
unchanged. On the legacy MMU path -- kvm_memslots_have_rmaps() ==
true, i.e. shadow paging, any VM that has allocated shadow roots, or
a write-tracked slot -- it reaches gfn_to_rmap(), which indexes
slot->arch.rmap[0][] with a near-U64_MAX gfn. That is an
out-of-bounds load of a kvm_rmap_head, followed by a conditional
clear of PT_WRITABLE_MASK in whatever the loaded pointer points at.
The path is reachable from any process holding /dev/kvm.
Range-check offset on its own first, so the addition cannot wrap.
memslot->npages is bounded well below U64_MAX, so once offset <
npages holds, offset + __fls(mask) (with __fls(mask) < BITS_PER_LONG)
stays in range.