Commit cc4cb017 authored by Maxim Levitsky's avatar Maxim Levitsky Committed by Paolo Bonzini
Browse files

KVM: x86: use positive error values for msr emulation that causes #GP



Recent introduction of the userspace msr filtering added code that uses
negative error codes for cases that result in either #GP delivery to
the guest, or handled by the userspace msr filtering.

This breaks an assumption that a negative error code returned from the
msr emulation code is a semi-fatal error which should be returned
to userspace via KVM_RUN ioctl and usually kill the guest.

Fix this by reusing the already existing KVM_MSR_RET_INVALID error code,
and by adding a new KVM_MSR_RET_FILTERED error code for the
userspace filtered msrs.

Fixes: 291f35fb2c1d1 ("KVM: x86: report negative values from wrmsr emulation to userspace")
Reported-by: default avatarQian Cai <cai@redhat.com>
Signed-off-by: default avatarMaxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20201101115523.115780-1-mlevitsk@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 177158e5
Loading
Loading
Loading
Loading
+15 −13
Original line number Original line Diff line number Diff line
@@ -255,10 +255,9 @@ static struct kmem_cache *x86_emulator_cache;


/*
/*
 * When called, it means the previous get/set msr reached an invalid msr.
 * When called, it means the previous get/set msr reached an invalid msr.
 * Return 0 if we want to ignore/silent this failed msr access, or 1 if we want
 * Return true if we want to ignore/silent this failed msr access.
 * to fail the caller.
 */
 */
static int kvm_msr_ignored_check(struct kvm_vcpu *vcpu, u32 msr,
static bool kvm_msr_ignored_check(struct kvm_vcpu *vcpu, u32 msr,
				  u64 data, bool write)
				  u64 data, bool write)
{
{
	const char *op = write ? "wrmsr" : "rdmsr";
	const char *op = write ? "wrmsr" : "rdmsr";
@@ -268,11 +267,11 @@ static int kvm_msr_ignored_check(struct kvm_vcpu *vcpu, u32 msr,
			kvm_pr_unimpl("ignored %s: 0x%x data 0x%llx\n",
			kvm_pr_unimpl("ignored %s: 0x%x data 0x%llx\n",
				      op, msr, data);
				      op, msr, data);
		/* Mask the error */
		/* Mask the error */
		return 0;
		return true;
	} else {
	} else {
		kvm_debug_ratelimited("unhandled %s: 0x%x data 0x%llx\n",
		kvm_debug_ratelimited("unhandled %s: 0x%x data 0x%llx\n",
				      op, msr, data);
				      op, msr, data);
		return -ENOENT;
		return false;
	}
	}
}
}


@@ -1416,7 +1415,8 @@ static int do_get_msr_feature(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
	if (r == KVM_MSR_RET_INVALID) {
	if (r == KVM_MSR_RET_INVALID) {
		/* Unconditionally clear the output for simplicity */
		/* Unconditionally clear the output for simplicity */
		*data = 0;
		*data = 0;
		r = kvm_msr_ignored_check(vcpu, index, 0, false);
		if (kvm_msr_ignored_check(vcpu, index, 0, false))
			r = 0;
	}
	}


	if (r)
	if (r)
@@ -1540,7 +1540,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,
	struct msr_data msr;
	struct msr_data msr;


	if (!host_initiated && !kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE))
	if (!host_initiated && !kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE))
		return -EPERM;
		return KVM_MSR_RET_FILTERED;


	switch (index) {
	switch (index) {
	case MSR_FS_BASE:
	case MSR_FS_BASE:
@@ -1581,7 +1581,8 @@ static int kvm_set_msr_ignored_check(struct kvm_vcpu *vcpu,
	int ret = __kvm_set_msr(vcpu, index, data, host_initiated);
	int ret = __kvm_set_msr(vcpu, index, data, host_initiated);


	if (ret == KVM_MSR_RET_INVALID)
	if (ret == KVM_MSR_RET_INVALID)
		ret = kvm_msr_ignored_check(vcpu, index, data, true);
		if (kvm_msr_ignored_check(vcpu, index, data, true))
			ret = 0;


	return ret;
	return ret;
}
}
@@ -1599,7 +1600,7 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data,
	int ret;
	int ret;


	if (!host_initiated && !kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ))
	if (!host_initiated && !kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ))
		return -EPERM;
		return KVM_MSR_RET_FILTERED;


	msr.index = index;
	msr.index = index;
	msr.host_initiated = host_initiated;
	msr.host_initiated = host_initiated;
@@ -1618,7 +1619,8 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu,
	if (ret == KVM_MSR_RET_INVALID) {
	if (ret == KVM_MSR_RET_INVALID) {
		/* Unconditionally clear *data for simplicity */
		/* Unconditionally clear *data for simplicity */
		*data = 0;
		*data = 0;
		ret = kvm_msr_ignored_check(vcpu, index, 0, false);
		if (kvm_msr_ignored_check(vcpu, index, 0, false))
			ret = 0;
	}
	}


	return ret;
	return ret;
@@ -1662,9 +1664,9 @@ static int complete_emulated_wrmsr(struct kvm_vcpu *vcpu)
static u64 kvm_msr_reason(int r)
static u64 kvm_msr_reason(int r)
{
{
	switch (r) {
	switch (r) {
	case -ENOENT:
	case KVM_MSR_RET_INVALID:
		return KVM_MSR_EXIT_REASON_UNKNOWN;
		return KVM_MSR_EXIT_REASON_UNKNOWN;
	case -EPERM:
	case KVM_MSR_RET_FILTERED:
		return KVM_MSR_EXIT_REASON_FILTER;
		return KVM_MSR_EXIT_REASON_FILTER;
	default:
	default:
		return KVM_MSR_EXIT_REASON_INVAL;
		return KVM_MSR_EXIT_REASON_INVAL;
+7 −1
Original line number Original line Diff line number Diff line
@@ -376,7 +376,13 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva);
int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva);
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type);
bool kvm_msr_allowed(struct kvm_vcpu *vcpu, u32 index, u32 type);


#define  KVM_MSR_RET_INVALID  2
/*
 * Internal error codes that are used to indicate that MSR emulation encountered
 * an error that should result in #GP in the guest, unless userspace
 * handles it.
 */
#define  KVM_MSR_RET_INVALID	2	/* in-kernel MSR emulation #GP condition */
#define  KVM_MSR_RET_FILTERED	3	/* #GP due to userspace MSR filter */


#define __cr4_reserved_bits(__cpu_has, __c)             \
#define __cr4_reserved_bits(__cpu_has, __c)             \
({                                                      \
({                                                      \