Commit 76d58e0f authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

KVM: fix KVM_CLEAR_DIRTY_LOG for memory slots of unaligned size



If a memory slot's size is not a multiple of 64 pages (256K), then
the KVM_CLEAR_DIRTY_LOG API is unusable: clearing the final 64 pages
either requires the requested page range to go beyond memslot->npages,
or requires log->num_pages to be unaligned, and kvm_clear_dirty_log_protect
requires log->num_pages to be both in range and aligned.

To allow this case, allow log->num_pages not to be a multiple of 64 if
it ends exactly on the last page of the slot.

Reported-by: default avatarPeter Xu <peterx@redhat.com>
Fixes: 98938aa8 ("KVM: validate userspace input in kvm_clear_dirty_log_protect()", 2019-01-02)
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 0699c64a
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -3830,8 +3830,9 @@ The ioctl clears the dirty status of pages in a memory slot, according to
the bitmap that is passed in struct kvm_clear_dirty_log's dirty_bitmap
the bitmap that is passed in struct kvm_clear_dirty_log's dirty_bitmap
field.  Bit 0 of the bitmap corresponds to page "first_page" in the
field.  Bit 0 of the bitmap corresponds to page "first_page" in the
memory slot, and num_pages is the size in bits of the input bitmap.
memory slot, and num_pages is the size in bits of the input bitmap.
Both first_page and num_pages must be a multiple of 64.  For each bit
first_page must be a multiple of 64; num_pages must also be a multiple of
that is set in the input bitmap, the corresponding page is marked "clean"
64 unless first_page + num_pages is the size of the memory slot.  For each
bit that is set in the input bitmap, the corresponding page is marked "clean"
in KVM's dirty bitmap, and dirty tracking is re-enabled for that page
in KVM's dirty bitmap, and dirty tracking is re-enabled for that page
(for example via write-protection, or by clearing the dirty bit in
(for example via write-protection, or by clearing the dirty bit in
a page table entry).
a page table entry).
+6 −3
Original line number Original line Diff line number Diff line
@@ -288,8 +288,11 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
#endif
#endif
	max_gfn = (1ul << (guest_pa_bits - guest_page_shift)) - 1;
	max_gfn = (1ul << (guest_pa_bits - guest_page_shift)) - 1;
	guest_page_size = (1ul << guest_page_shift);
	guest_page_size = (1ul << guest_page_shift);
	/* 1G of guest page sized pages */
	/*
	guest_num_pages = (1ul << (30 - guest_page_shift));
	 * A little more than 1G of guest page sized pages.  Cover the
	 * case where the size is not aligned to 64 pages.
	 */
	guest_num_pages = (1ul << (30 - guest_page_shift)) + 3;
	host_page_size = getpagesize();
	host_page_size = getpagesize();
	host_num_pages = (guest_num_pages * guest_page_size) / host_page_size +
	host_num_pages = (guest_num_pages * guest_page_size) / host_page_size +
			 !!((guest_num_pages * guest_page_size) % host_page_size);
			 !!((guest_num_pages * guest_page_size) % host_page_size);
@@ -359,7 +362,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
		kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
		kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
#ifdef USE_CLEAR_DIRTY_LOG
#ifdef USE_CLEAR_DIRTY_LOG
		kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
		kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
				       DIV_ROUND_UP(host_num_pages, 64) * 64);
				       host_num_pages);
#endif
#endif
		vm_dirty_log_verify(bmap);
		vm_dirty_log_verify(bmap);
		iteration++;
		iteration++;
+4 −3
Original line number Original line Diff line number Diff line
@@ -1240,7 +1240,7 @@ int kvm_clear_dirty_log_protect(struct kvm *kvm,
	if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
	if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
		return -EINVAL;
		return -EINVAL;


	if ((log->first_page & 63) || (log->num_pages & 63))
	if (log->first_page & 63)
		return -EINVAL;
		return -EINVAL;


	slots = __kvm_memslots(kvm, as_id);
	slots = __kvm_memslots(kvm, as_id);
@@ -1253,7 +1253,8 @@ int kvm_clear_dirty_log_protect(struct kvm *kvm,
	n = kvm_dirty_bitmap_bytes(memslot);
	n = kvm_dirty_bitmap_bytes(memslot);


	if (log->first_page > memslot->npages ||
	if (log->first_page > memslot->npages ||
	    log->num_pages > memslot->npages - log->first_page)
	    log->num_pages > memslot->npages - log->first_page ||
	    (log->num_pages < memslot->npages - log->first_page && (log->num_pages & 63)))
	    return -EINVAL;
	    return -EINVAL;


	*flush = false;
	*flush = false;