diff mbox series

[v6,5/7] KVM: x86: VMX: Prevent MSR passthrough when MSR access is denied

Message ID 20200902125935.20646-6-graf@amazon.com (mailing list archive)
State New, archived
Headers show
Series Allow user space to restrict and augment MSR emulation | expand

Commit Message

Alexander Graf Sept. 2, 2020, 12:59 p.m. UTC
We will introduce the concept of MSRs that may not be handled in kernel
space soon. Some MSRs are directly passed through to the guest, effectively
making them handled by KVM from user space's point of view.

This patch introduces all logic required to ensure that MSRs that
user space wants trapped are not marked as direct access for guests.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 arch/x86/kvm/vmx/vmx.c | 226 +++++++++++++++++++++++++++++++----------
 arch/x86/kvm/vmx/vmx.h |   7 ++
 2 files changed, 181 insertions(+), 52 deletions(-)

Comments

Aaron Lewis Sept. 4, 2020, 2:18 a.m. UTC | #1
> +/*
> + * List of MSRs that can be directly passed to the guest.
> + * In addition to these x2apic and PT MSRs are handled specially.
> + */
> +static u32 vmx_possible_passthrough_msrs[MAX_POSSIBLE_PASSGHROUGH_MSRS] = {

MAX_POSSIBLE_PASSGHROUGH_MSRS should be MAX_POSSIBLE_PASSTHROUGH_MSRS

> +       MSR_IA32_SPEC_CTRL,
> +       MSR_IA32_PRED_CMD,
> +       MSR_IA32_TSC,
> +       MSR_FS_BASE,
> +       MSR_GS_BASE,
> +       MSR_KERNEL_GS_BASE,
> +       MSR_IA32_SYSENTER_CS,
> +       MSR_IA32_SYSENTER_ESP,
> +       MSR_IA32_SYSENTER_EIP,
> +       MSR_CORE_C1_RES,
> +       MSR_CORE_C3_RESIDENCY,
> +       MSR_CORE_C6_RESIDENCY,
> +       MSR_CORE_C7_RESIDENCY,
> +};

Is there any reason not to construct this list on the fly?  That could
help prevent the list from becoming stale over time if this is missed
when calls to vmx_disable_intercept_for_msr() are added.

> +
>  /*
>   * These 2 parameters are used to config the controls for Pause-Loop Exiting:
>   * ple_gap:    upper bound on the amount of time between two successive
> @@ -622,6 +642,41 @@ static inline bool report_flexpriority(void)
>         return flexpriority_enabled;
>  }

One thing that seems to be missing is removing MSRs from the
permission bitmap or resetting the permission bitmap to its original
state before adding changes on top of it.  This would be needed on
subsequent calls to kvm_vm_ioctl_set_msr_filter().  When that happens
the original changes made by KVM_REQ_MSR_FILTER_CHANGED need to be
backed out before applying the new set.
Alexander Graf Sept. 16, 2020, 7:44 p.m. UTC | #2
On 04.09.20 04:18, Aaron Lewis wrote:
> 
>> +/*
>> + * List of MSRs that can be directly passed to the guest.
>> + * In addition to these x2apic and PT MSRs are handled specially.
>> + */
>> +static u32 vmx_possible_passthrough_msrs[MAX_POSSIBLE_PASSGHROUGH_MSRS] = {
> 
> MAX_POSSIBLE_PASSGHROUGH_MSRS should be MAX_POSSIBLE_PASSTHROUGH_MSRS

Ouch. Thanks :).

> 
>> +       MSR_IA32_SPEC_CTRL,
>> +       MSR_IA32_PRED_CMD,
>> +       MSR_IA32_TSC,
>> +       MSR_FS_BASE,
>> +       MSR_GS_BASE,
>> +       MSR_KERNEL_GS_BASE,
>> +       MSR_IA32_SYSENTER_CS,
>> +       MSR_IA32_SYSENTER_ESP,
>> +       MSR_IA32_SYSENTER_EIP,
>> +       MSR_CORE_C1_RES,
>> +       MSR_CORE_C3_RESIDENCY,
>> +       MSR_CORE_C6_RESIDENCY,
>> +       MSR_CORE_C7_RESIDENCY,
>> +};
> 
> Is there any reason not to construct this list on the fly?  That could
> help prevent the list from becoming stale over time if this is missed
> when calls to vmx_disable_intercept_for_msr() are added.

The problem is that we have an upper bound of elements that we can store 
in the bitmap. We can either make that number arbitrary and then have 
really awkward failure modes or be incredibly picky instead.

I went for incredibly picky. If anything goes out of sync, like when 
someone adds an MSR to the list without changing 
MAX_POSSIBLE_PASSTHROUGH_MSRS, a call to 
vmx_{en,dis}able_intercept_for_msr() is done on an MSR that is not part 
of the list, we will abort early and in the former case already through 
the compiler.

If you can think of a good way to construct the list dynamically and 
still have a working bitmap of "desired" passthrough states, I'm all 
ears :).

> 
>> +
>>   /*
>>    * These 2 parameters are used to config the controls for Pause-Loop Exiting:
>>    * ple_gap:    upper bound on the amount of time between two successive
>> @@ -622,6 +642,41 @@ static inline bool report_flexpriority(void)
>>          return flexpriority_enabled;
>>   }
> 
> One thing that seems to be missing is removing MSRs from the
> permission bitmap or resetting the permission bitmap to its original
> state before adding changes on top of it.  This would be needed on
> subsequent calls to kvm_vm_ioctl_set_msr_filter().  When that happens
> the original changes made by KVM_REQ_MSR_FILTER_CHANGED need to be
> backed out before applying the new set.

I'm not sure I follow. Subsequent calls to set_msr_filter() will invoke 
the "please reset the whole MSR passthrough bitmap to a consistent 
state" which will then reapply the in-kvm desired state through the 
bitmap and filter state on top on each of those.


Alex




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Aaron Lewis Sept. 16, 2020, 8:13 p.m. UTC | #3
> >
> >> +
> >>   /*
> >>    * These 2 parameters are used to config the controls for Pause-Loop Exiting:
> >>    * ple_gap:    upper bound on the amount of time between two successive
> >> @@ -622,6 +642,41 @@ static inline bool report_flexpriority(void)
> >>          return flexpriority_enabled;
> >>   }
> >
> > One thing that seems to be missing is removing MSRs from the
> > permission bitmap or resetting the permission bitmap to its original
> > state before adding changes on top of it.  This would be needed on
> > subsequent calls to kvm_vm_ioctl_set_msr_filter().  When that happens
> > the original changes made by KVM_REQ_MSR_FILTER_CHANGED need to be
> > backed out before applying the new set.
>
> I'm not sure I follow. Subsequent calls to set_msr_filter() will invoke
> the "please reset the whole MSR passthrough bitmap to a consistent
> state" which will then reapply the in-kvm desired state through the
> bitmap and filter state on top on each of those.
>

Yes, you're correct.  I discovered this after the fact by adding a
test to the selftest I wrote for the deny list system which I revamped
to work for your filter system.  It proved the permission bitmaps are
in fact set as expected on subsequent calls.  You can disregard this
comment.

As a side note, I'm happy to share the test if you'd like. I also used
it to uncover an issue in the first commit of this series.
Alexander Graf Sept. 16, 2020, 8:36 p.m. UTC | #4
On 16.09.20 22:13, Aaron Lewis wrote:
> 
>>>
>>>> +
>>>>    /*
>>>>     * These 2 parameters are used to config the controls for Pause-Loop Exiting:
>>>>     * ple_gap:    upper bound on the amount of time between two successive
>>>> @@ -622,6 +642,41 @@ static inline bool report_flexpriority(void)
>>>>           return flexpriority_enabled;
>>>>    }
>>>
>>> One thing that seems to be missing is removing MSRs from the
>>> permission bitmap or resetting the permission bitmap to its original
>>> state before adding changes on top of it.  This would be needed on
>>> subsequent calls to kvm_vm_ioctl_set_msr_filter().  When that happens
>>> the original changes made by KVM_REQ_MSR_FILTER_CHANGED need to be
>>> backed out before applying the new set.
>>
>> I'm not sure I follow. Subsequent calls to set_msr_filter() will invoke
>> the "please reset the whole MSR passthrough bitmap to a consistent
>> state" which will then reapply the in-kvm desired state through the
>> bitmap and filter state on top on each of those.
>>
> 
> Yes, you're correct.  I discovered this after the fact by adding a
> test to the selftest I wrote for the deny list system which I revamped
> to work for your filter system.  It proved the permission bitmaps are
> in fact set as expected on subsequent calls.  You can disregard this
> comment.
> 
> As a side note, I'm happy to share the test if you'd like. I also used
> it to uncover an issue in the first commit of this series.

I really enjoy the tests that you submitted and would love to see you 
add your test coverage to the filtering patch set :)


Thanks!

Alex




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
diff mbox series

Patch

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 1313e47a5a1e..6523f792652d 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -148,6 +148,26 @@  module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
 #define MSR_IA32_RTIT_OUTPUT_BASE_MASK \
 	(~((1UL << cpuid_query_maxphyaddr(vcpu)) - 1) | 0x7f)
 
+/*
+ * List of MSRs that can be directly passed to the guest.
+ * In addition to these x2apic and PT MSRs are handled specially.
+ */
+static u32 vmx_possible_passthrough_msrs[MAX_POSSIBLE_PASSGHROUGH_MSRS] = {
+	MSR_IA32_SPEC_CTRL,
+	MSR_IA32_PRED_CMD,
+	MSR_IA32_TSC,
+	MSR_FS_BASE,
+	MSR_GS_BASE,
+	MSR_KERNEL_GS_BASE,
+	MSR_IA32_SYSENTER_CS,
+	MSR_IA32_SYSENTER_ESP,
+	MSR_IA32_SYSENTER_EIP,
+	MSR_CORE_C1_RES,
+	MSR_CORE_C3_RESIDENCY,
+	MSR_CORE_C6_RESIDENCY,
+	MSR_CORE_C7_RESIDENCY,
+};
+
 /*
  * These 2 parameters are used to config the controls for Pause-Loop Exiting:
  * ple_gap:    upper bound on the amount of time between two successive
@@ -622,6 +642,41 @@  static inline bool report_flexpriority(void)
 	return flexpriority_enabled;
 }
 
+static int possible_passthrough_msr_idx(u32 msr)
+{
+	u32 i;
+
+	for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++)
+		if (vmx_possible_passthrough_msrs[i] == msr)
+			return i;
+
+	return -ENOENT;
+}
+
+static bool is_valid_passthrough_msr(u32 msr)
+{
+	bool r;
+
+	switch (msr) {
+	case 0x800 ... 0x8ff:
+		/* x2APIC MSRs. These are handled in vmx_update_msr_bitmap_x2apic() */
+		return true;
+	case MSR_IA32_RTIT_STATUS:
+	case MSR_IA32_RTIT_OUTPUT_BASE:
+	case MSR_IA32_RTIT_OUTPUT_MASK:
+	case MSR_IA32_RTIT_CR3_MATCH:
+	case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B:
+		/* PT MSRs. These are handled in pt_update_intercept_for_msr() */
+		return true;
+	}
+
+	r = possible_passthrough_msr_idx(msr) != -ENOENT;
+
+	WARN(!r, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
+
+	return r;
+}
+
 static inline int __find_msr_index(struct vcpu_vmx *vmx, u32 msr)
 {
 	int i;
@@ -3690,12 +3745,51 @@  void free_vpid(int vpid)
 	spin_unlock(&vmx_vpid_lock);
 }
 
+static void vmx_clear_msr_bitmap_read(ulong *msr_bitmap, u32 msr)
+{
+	int f = sizeof(unsigned long);
+
+	if (msr <= 0x1fff)
+		__clear_bit(msr, msr_bitmap + 0x000 / f);
+	else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff))
+		__clear_bit(msr & 0x1fff, msr_bitmap + 0x400 / f);
+}
+
+static void vmx_clear_msr_bitmap_write(ulong *msr_bitmap, u32 msr)
+{
+	int f = sizeof(unsigned long);
+
+	if (msr <= 0x1fff)
+		__clear_bit(msr, msr_bitmap + 0x800 / f);
+	else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff))
+		__clear_bit(msr & 0x1fff, msr_bitmap + 0xc00 / f);
+}
+
+static void vmx_set_msr_bitmap_read(ulong *msr_bitmap, u32 msr)
+{
+	int f = sizeof(unsigned long);
+
+	if (msr <= 0x1fff)
+		__set_bit(msr, msr_bitmap + 0x000 / f);
+	else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff))
+		__set_bit(msr & 0x1fff, msr_bitmap + 0x400 / f);
+}
+
+static void vmx_set_msr_bitmap_write(ulong *msr_bitmap, u32 msr)
+{
+	int f = sizeof(unsigned long);
+
+	if (msr <= 0x1fff)
+		__set_bit(msr, msr_bitmap + 0x800 / f);
+	else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff))
+		__set_bit(msr & 0x1fff, msr_bitmap + 0xc00 / f);
+}
+
 static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu,
 							  u32 msr, int type)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
-	int f = sizeof(unsigned long);
 
 	if (!cpu_has_vmx_msr_bitmap())
 		return;
@@ -3704,30 +3798,37 @@  static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu,
 		evmcs_touch_msr_bitmap();
 
 	/*
-	 * See Intel PRM Vol. 3, 20.6.9 (MSR-Bitmap Address). Early manuals
-	 * have the write-low and read-high bitmap offsets the wrong way round.
-	 * We can control MSRs 0x00000000-0x00001fff and 0xc0000000-0xc0001fff.
-	 */
-	if (msr <= 0x1fff) {
-		if (type & MSR_TYPE_R)
-			/* read-low */
-			__clear_bit(msr, msr_bitmap + 0x000 / f);
+	 * Mark the desired intercept state in shadow bitmap, this is needed
+	 * for resync when the MSR filters change.
+	*/
+	if (is_valid_passthrough_msr(msr)) {
+		int idx = possible_passthrough_msr_idx(msr);
+
+		if (idx != -ENOENT) {
+			if (type & MSR_TYPE_R)
+				clear_bit(idx, vmx->shadow_msr_intercept.read);
+			if (type & MSR_TYPE_W)
+				clear_bit(idx, vmx->shadow_msr_intercept.write);
+		}
+	}
 
-		if (type & MSR_TYPE_W)
-			/* write-low */
-			__clear_bit(msr, msr_bitmap + 0x800 / f);
+	if ((type & MSR_TYPE_R) &&
+	    !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) {
+		vmx_set_msr_bitmap_read(msr_bitmap, msr);
+		type &= ~MSR_TYPE_R;
+	}
 
-	} else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
-		msr &= 0x1fff;
-		if (type & MSR_TYPE_R)
-			/* read-high */
-			__clear_bit(msr, msr_bitmap + 0x400 / f);
+	if ((type & MSR_TYPE_W) &&
+	    !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) {
+		vmx_set_msr_bitmap_write(msr_bitmap, msr);
+		type &= ~MSR_TYPE_W;
+	}
 
-		if (type & MSR_TYPE_W)
-			/* write-high */
-			__clear_bit(msr, msr_bitmap + 0xc00 / f);
+	if (type & MSR_TYPE_R)
+		vmx_clear_msr_bitmap_read(msr_bitmap, msr);
 
-	}
+	if (type & MSR_TYPE_W)
+		vmx_clear_msr_bitmap_write(msr_bitmap, msr);
 }
 
 static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu,
@@ -3735,7 +3836,6 @@  static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu,
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
-	int f = sizeof(unsigned long);
 
 	if (!cpu_has_vmx_msr_bitmap())
 		return;
@@ -3744,30 +3844,25 @@  static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu,
 		evmcs_touch_msr_bitmap();
 
 	/*
-	 * See Intel PRM Vol. 3, 20.6.9 (MSR-Bitmap Address). Early manuals
-	 * have the write-low and read-high bitmap offsets the wrong way round.
-	 * We can control MSRs 0x00000000-0x00001fff and 0xc0000000-0xc0001fff.
-	 */
-	if (msr <= 0x1fff) {
-		if (type & MSR_TYPE_R)
-			/* read-low */
-			__set_bit(msr, msr_bitmap + 0x000 / f);
-
-		if (type & MSR_TYPE_W)
-			/* write-low */
-			__set_bit(msr, msr_bitmap + 0x800 / f);
-
-	} else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
-		msr &= 0x1fff;
-		if (type & MSR_TYPE_R)
-			/* read-high */
-			__set_bit(msr, msr_bitmap + 0x400 / f);
+	 * Mark the desired intercept state in shadow bitmap, this is needed
+	 * for resync when the MSR filter changes.
+	*/
+	if (is_valid_passthrough_msr(msr)) {
+		int idx = possible_passthrough_msr_idx(msr);
+
+		if (idx != -ENOENT) {
+			if (type & MSR_TYPE_R)
+				set_bit(idx, vmx->shadow_msr_intercept.read);
+			if (type & MSR_TYPE_W)
+				set_bit(idx, vmx->shadow_msr_intercept.write);
+		}
+	}
 
-		if (type & MSR_TYPE_W)
-			/* write-high */
-			__set_bit(msr, msr_bitmap + 0xc00 / f);
+	if (type & MSR_TYPE_R)
+		vmx_set_msr_bitmap_read(msr_bitmap, msr);
 
-	}
+	if (type & MSR_TYPE_W)
+		vmx_set_msr_bitmap_write(msr_bitmap, msr);
 }
 
 static __always_inline void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu,
@@ -3794,15 +3889,14 @@  static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu)
 	return mode;
 }
 
-static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu,
-					 unsigned long *msr_bitmap, u8 mode)
+static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu, u8 mode)
 {
 	int msr;
 
-	for (msr = 0x800; msr <= 0x8ff; msr += BITS_PER_LONG) {
-		unsigned word = msr / BITS_PER_LONG;
-		msr_bitmap[word] = (mode & MSR_BITMAP_MODE_X2APIC_APICV) ? 0 : ~0;
-		msr_bitmap[word + (0x800 / sizeof(long))] = ~0;
+	for (msr = 0x800; msr <= 0x8ff; msr++) {
+		bool intercepted = !!(mode & MSR_BITMAP_MODE_X2APIC_APICV);
+
+		vmx_set_intercept_for_msr(vcpu, msr, MSR_TYPE_RW, intercepted);
 	}
 
 	if (mode & MSR_BITMAP_MODE_X2APIC) {
@@ -3822,7 +3916,6 @@  static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu,
 void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
-	unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
 	u8 mode = vmx_msr_bitmap_mode(vcpu);
 	u8 changed = mode ^ vmx->msr_bitmap_mode;
 
@@ -3830,7 +3923,7 @@  void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu)
 		return;
 
 	if (changed & (MSR_BITMAP_MODE_X2APIC | MSR_BITMAP_MODE_X2APIC_APICV))
-		vmx_update_msr_bitmap_x2apic(vcpu, msr_bitmap, mode);
+		vmx_update_msr_bitmap_x2apic(vcpu, mode);
 
 	vmx->msr_bitmap_mode = mode;
 }
@@ -3871,6 +3964,29 @@  static bool vmx_guest_apic_has_interrupt(struct kvm_vcpu *vcpu)
 	return ((rvi & 0xf0) > (vppr & 0xf0));
 }
 
+static void vmx_msr_filter_changed(struct kvm_vcpu *vcpu)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	u32 i;
+
+	/*
+	 * Set intercept permissions for all potentially passed through MSRs
+	 * again. They will automatically get filtered through the MSR filter,
+	 * so we are back in sync after this.
+	 */
+	for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) {
+		u32 msr = vmx_possible_passthrough_msrs[i];
+		bool read = test_bit(i, vmx->shadow_msr_intercept.read);
+		bool write = test_bit(i, vmx->shadow_msr_intercept.write);
+
+		vmx_set_intercept_for_msr(vcpu, msr, MSR_TYPE_R, read);
+		vmx_set_intercept_for_msr(vcpu, msr, MSR_TYPE_W, write);
+	}
+
+	pt_update_intercept_for_msr(vcpu);
+	vmx_update_msr_bitmap_x2apic(vcpu, vmx_msr_bitmap_mode(vcpu));
+}
+
 static inline bool kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu,
 						     bool nested)
 {
@@ -6901,6 +7017,10 @@  static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
 	if (err < 0)
 		goto free_pml;
 
+	/* The MSR bitmap starts with all ones */
+	bitmap_fill(vmx->shadow_msr_intercept.read, MAX_POSSIBLE_PASSGHROUGH_MSRS);
+	bitmap_fill(vmx->shadow_msr_intercept.write, MAX_POSSIBLE_PASSGHROUGH_MSRS);
+
 	msr_bitmap = vmx->vmcs01.msr_bitmap;
 	vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC, MSR_TYPE_R);
 	vmx_disable_intercept_for_msr(vcpu, MSR_FS_BASE, MSR_TYPE_RW);
@@ -7965,6 +8085,8 @@  static struct kvm_x86_ops vmx_x86_ops __initdata = {
 	.need_emulation_on_page_fault = vmx_need_emulation_on_page_fault,
 	.apic_init_signal_blocked = vmx_apic_init_signal_blocked,
 	.migrate_timers = vmx_migrate_timers,
+
+	.msr_filter_changed = vmx_msr_filter_changed,
 };
 
 static __init int hardware_setup(void)
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index b3c74f0fe8a1..05694d2fd4ff 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -300,6 +300,13 @@  struct vcpu_vmx {
 	u64 ept_pointer;
 
 	struct pt_desc pt_desc;
+
+	/* Save desired MSR intercept (read: pass-through) state */
+#define MAX_POSSIBLE_PASSGHROUGH_MSRS	13
+	struct {
+		DECLARE_BITMAP(read, MAX_POSSIBLE_PASSGHROUGH_MSRS);
+		DECLARE_BITMAP(write, MAX_POSSIBLE_PASSGHROUGH_MSRS);
+	} shadow_msr_intercept;
 };
 
 enum ept_pointers_status {