Re: [PATCH 1/7] KVM: Fix out of range accesses to memslots

From: Claudio Imbrenda
Date: Tue Mar 24 2020 - 07:47:01 EST


On Tue, 24 Mar 2020 08:12:59 +0100
Christian Borntraeger <borntraeger@xxxxxxxxxx> wrote:

> On 20.03.20 21:55, Sean Christopherson wrote:
> > Reset the LRU slot if it becomes invalid when deleting a memslot to
> > fix an out-of-bounds/use-after-free access when searching through
> > memslots.
> >
> > Explicitly check for there being no used slots in
> > search_memslots(), and in the caller of s390's approximation
> > variant.
> >
> > Fixes: 36947254e5f9 ("KVM: Dynamically size memslot array based on
> > number of used slots") Reported-by: Qian Cai <cai@xxxxxx>
> > Cc: Peter Xu <peterx@xxxxxxxxxx>
> > Signed-off-by: Sean Christopherson <sean.j.christopherson@xxxxxxxxx>
> > ---
> > arch/s390/kvm/kvm-s390.c | 3 +++
> > include/linux/kvm_host.h | 3 +++
> > virt/kvm/kvm_main.c | 3 +++
> > 3 files changed, 9 insertions(+)
> >
> > diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> > index 807ed6d722dd..cb15fdda1fee 100644
> > --- a/arch/s390/kvm/kvm-s390.c
> > +++ b/arch/s390/kvm/kvm-s390.c
> > @@ -2002,6 +2002,9 @@ static int kvm_s390_get_cmma(struct kvm *kvm,
> > struct kvm_s390_cmma_log *args,
>
> Adding Claudio, but
> > struct kvm_memslots *slots = kvm_memslots(kvm);
> > struct kvm_memory_slot *ms;
> >
> > + if (unlikely(!slots->used_slots))
> > + return 0;
> > +

this should never happen, as this function is only called during
migration, and if we don't have any memory slots, then we will not try
to migrate them.

But this is something that is triggered by userspace, so we need to
protect the kernel from rogue or broken userspace.

Reviewed-by: Claudio Imbrenda <imbrenda@xxxxxxxxxxxxx>

> this looks sane and like the right fix.
>
> Acked-by: Christian Borntraeger <borntraeger@xxxxxxxxxx>
>
> > cur_gfn = kvm_s390_next_dirty_cmma(slots, args->start_gfn);
> > ms = gfn_to_memslot(kvm, cur_gfn);
> > args->count = 0;
> > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> > index 35bc52e187a2..b19dee4ed7d9 100644
> > --- a/include/linux/kvm_host.h
> > +++ b/include/linux/kvm_host.h
> > @@ -1032,6 +1032,9 @@ search_memslots(struct kvm_memslots *slots,
> > gfn_t gfn) int slot = atomic_read(&slots->lru_slot);
> > struct kvm_memory_slot *memslots = slots->memslots;
> >
> > + if (unlikely(!slots->used_slots))
> > + return NULL;
> > +
> > if (gfn >= memslots[slot].base_gfn &&
> > gfn < memslots[slot].base_gfn + memslots[slot].npages)
> > return &memslots[slot];
> > diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> > index 28eae681859f..f744bc603c53 100644
> > --- a/virt/kvm/kvm_main.c
> > +++ b/virt/kvm/kvm_main.c
> > @@ -882,6 +882,9 @@ static inline void kvm_memslot_delete(struct
> > kvm_memslots *slots,
> > slots->used_slots--;
> >
> > + if (atomic_read(&slots->lru_slot) >= slots->used_slots)
> > + atomic_set(&slots->lru_slot, 0);
> > +
> > for (i = slots->id_to_index[memslot->id]; i <
> > slots->used_slots; i++) { mslots[i] = mslots[i + 1];
> > slots->id_to_index[mslots[i].id] = i;
> >