Commit a5264387 authored by Manuel Andreas's avatar Manuel Andreas Committed by Paolo Bonzini
Browse files

KVM: x86: hyper-v: Validate all GVAs during PV TLB flush



In KVM guests with Hyper-V hypercalls enabled, the hypercalls
HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST and HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX
allow a guest to request invalidation of portions of a virtual TLB.
For this, the hypercall parameter includes a list of GVAs that are supposed
to be invalidated.

Currently, only the base GVA is checked to be canonical. In reality, this
check needs to be performed for the entire range of GVAs, as checking only
the base GVA enables guests running on Intel hardware to trigger a
WARN_ONCE in the host (see Fixes commit below).

Move the check for non-canonical addresses to be performed for every GVA
of the supplied range to avoid the splat, and to be more in line with the
Hyper-V specification, since, although unlikely, a range starting with an
invalid GVA may still contain GVAs that are valid.

Fixes: fa787ac0 ("KVM: x86/hyper-v: Skip non-canonical addresses during PV TLB flush")
Signed-off-by: default avatarManuel Andreas <manuel.andreas@tum.de>
Reviewed-by: default avatarVitaly Kuznetsov <vkuznets@redhat.com>
Link: https://patch.msgid.link/00a7a31b-573b-4d92-91f8-7d7e2f88ea48@tum.de


[sean: massage changelog]
Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 4b3b8a8b
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -1981,16 +1981,17 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
		if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY)
			goto out_flush_all;

		if (is_noncanonical_invlpg_address(entries[i], vcpu))
			continue;

		/*
		 * Lower 12 bits of 'address' encode the number of additional
		 * pages to flush.
		 */
		gva = entries[i] & PAGE_MASK;
		for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++)
		for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++) {
			if (is_noncanonical_invlpg_address(gva + j * PAGE_SIZE, vcpu))
				continue;

			kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE);
		}

		++vcpu->stat.tlb_flush;
	}