VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46014

CVE-2026-46014

Description

In the Linux kernel, the following vulnerability has been resolved:

KVM: SVM: Add missing save/restore handling of LBR MSRs

MSR_IA32_DEBUGCTLMSR and LBR MSRs are currently not enumerated by KVM_GET_MSR_INDEX_LIST, and LBR MSRs cannot be set with KVM_SET_MSRS. So save/restore is completely broken.

Fix it by adding the MSRs to msrs_to_save_base, and allowing writes to LBR MSRs from userspace only (as they are read-only MSRs) if LBR virtualization is enabled. Additionally, to correctly restore L1's LBRs while L2 is running, make sure the LBRs are copied from the captured VMCB01 save area in svm_copy_vmrun_state().

Note, for VMX, this also fixes a flaw where MSR_IA32_DEBUGCTLMSR isn't reported as an MSR to save/restore.

Note #2, over-reporting MSR_IA32_LASTxxx on Intel is ok, as KVM already handles unsupported reads and writes thanks to commit b5e2fec0ebc3 ("KVM: Ignore DEBUGCTL MSRs with no effect") (kvm_do_msr_access() will morph the unsupported userspace write into a nop).

[sean: guard with lbrv checks, massage changelog]

Affected products

1

Patches

6
13a89ada5dcf

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 6bdcde83e4ef94..40feb2ae31a015 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1109,6 +1109,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 6bdcde83e4ef94..40feb2ae31a015 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1109,6 +1109,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index f998b63f3bb4d5..1f5b8bab27a07f 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2788,19 +2788,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3075,6 +3075,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index f998b63f3bb4d5..1f5b8bab27a07f 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2788,19 +2788,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3075,6 +3075,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 3c9bcb82a41b0a..ccc7b2e34a11e8 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 3c9bcb82a41b0a..ccc7b2e34a11e8 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
2b922a42b531

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index f208b0dec2f004..2c3a41305317de 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1057,6 +1057,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index f208b0dec2f004..2c3a41305317de 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1057,6 +1057,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index addb9827c3d591..35e24f8f060ad5 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2712,19 +2712,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -2999,6 +2999,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index addb9827c3d591..35e24f8f060ad5 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2712,19 +2712,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -2999,6 +2999,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 24ed8139528809..ad2b7158b9c8ea 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -350,6 +350,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 24ed8139528809..ad2b7158b9c8ea 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -350,6 +350,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
3700f0788da6

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 9c64d036e30bd3..2b1066ce23f5bf 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1099,6 +1099,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 9c64d036e30bd3..2b1066ce23f5bf 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1099,6 +1099,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index 7170f2f623afe3..e97c56df41f632 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2787,19 +2787,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3074,6 +3074,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index 7170f2f623afe3..e97c56df41f632 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2787,19 +2787,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3074,6 +3074,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 6e87ec52fa0640..64da02d1ee0084 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 6e87ec52fa0640..64da02d1ee0084 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
3700f0788da6

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 9c64d036e30bd3..2b1066ce23f5bf 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1099,6 +1099,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 9c64d036e30bd3..2b1066ce23f5bf 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1099,6 +1099,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index 7170f2f623afe3..e97c56df41f632 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2787,19 +2787,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3074,6 +3074,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index 7170f2f623afe3..e97c56df41f632 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2787,19 +2787,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3074,6 +3074,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 6e87ec52fa0640..64da02d1ee0084 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 6e87ec52fa0640..64da02d1ee0084 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
13a89ada5dcf

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 6bdcde83e4ef94..40feb2ae31a015 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1109,6 +1109,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index 6bdcde83e4ef94..40feb2ae31a015 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1109,6 +1109,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index f998b63f3bb4d5..1f5b8bab27a07f 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2788,19 +2788,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3075,6 +3075,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index f998b63f3bb4d5..1f5b8bab27a07f 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2788,19 +2788,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -3075,6 +3075,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 3c9bcb82a41b0a..ccc7b2e34a11e8 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 3c9bcb82a41b0a..ccc7b2e34a11e8 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
2b922a42b531

KVM: SVM: Add missing save/restore handling of LBR MSRs

6 files changed · +90 12
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index f208b0dec2f004..2c3a41305317de 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1057,6 +1057,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/nested.c+5 0 modified
    diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
    index f208b0dec2f004..2c3a41305317de 100644
    --- a/arch/x86/kvm/svm/nested.c
    +++ b/arch/x86/kvm/svm/nested.c
    @@ -1057,6 +1057,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
     		to_save->isst_addr = from_save->isst_addr;
     		to_save->ssp = from_save->ssp;
     	}
    +
    +	if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
    +		svm_copy_lbrs(to_save, from_save);
    +		to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
    +	}
     }
     
     void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index addb9827c3d591..35e24f8f060ad5 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2712,19 +2712,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -2999,6 +2999,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/svm/svm.c+37 5 modified
    diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
    index addb9827c3d591..35e24f8f060ad5 100644
    --- a/arch/x86/kvm/svm/svm.c
    +++ b/arch/x86/kvm/svm/svm.c
    @@ -2712,19 +2712,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
     		msr_info->data = svm->tsc_aux;
     		break;
     	case MSR_IA32_DEBUGCTLMSR:
    -		msr_info->data = svm->vmcb->save.dbgctl;
    +		msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
     		break;
     	case MSR_IA32_LASTBRANCHFROMIP:
    -		msr_info->data = svm->vmcb->save.br_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
     		break;
     	case MSR_IA32_LASTBRANCHTOIP:
    -		msr_info->data = svm->vmcb->save.br_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
     		break;
     	case MSR_IA32_LASTINTFROMIP:
    -		msr_info->data = svm->vmcb->save.last_excp_from;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
     		break;
     	case MSR_IA32_LASTINTTOIP:
    -		msr_info->data = svm->vmcb->save.last_excp_to;
    +		msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
     		break;
     	case MSR_VM_HSAVE_PA:
     		msr_info->data = svm->nested.hsave_msr;
    @@ -2999,6 +2999,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
     		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
     		svm_update_lbrv(vcpu);
     		break;
    +	case MSR_IA32_LASTBRANCHFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTBRANCHTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.br_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTFROMIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_from = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
    +	case MSR_IA32_LASTINTTOIP:
    +		if (!lbrv)
    +			return KVM_MSR_RET_UNSUPPORTED;
    +		if (!msr->host_initiated)
    +			return 1;
    +		svm->vmcb->save.last_excp_to = data;
    +		vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
    +		break;
     	case MSR_VM_HSAVE_PA:
     		/*
     		 * Old kernels did not validate the value written to
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 24ed8139528809..ad2b7158b9c8ea 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -350,6 +350,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    
  • arch/x86/kvm/x86.c+3 1 modified
    diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
    index 24ed8139528809..ad2b7158b9c8ea 100644
    --- a/arch/x86/kvm/x86.c
    +++ b/arch/x86/kvm/x86.c
    @@ -350,6 +350,9 @@ static const u32 msrs_to_save_base[] = {
     	MSR_IA32_U_CET, MSR_IA32_S_CET,
     	MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
     	MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
    +	MSR_IA32_DEBUGCTLMSR,
    +	MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
    +	MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
     };
     
     static const u32 msrs_to_save_pmu[] = {
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"MSR_IA32_DEBUGCTLMSR and LBR MSRs were not enumerated in KVM_GET_MSR_INDEX_LIST and LBR MSRs could not be set via KVM_SET_MSRS, making save/restore completely broken."

Attack vector

An attacker with access to the KVM device (e.g., a VM administrator) could migrate or save/restore a VM's state, and the LBR-related MSRs (MSR_IA32_DEBUGCTLMSR, MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP, MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP) would be silently dropped because they were not in the msrs_to_save_base list [patch_id=2660435]. Additionally, LBR MSRs could not be written via KVM_SET_MSRS at all, so even if a userspace VMM attempted to restore them, the write would fail. The bug is triggered through normal KVM ioctl operations (KVM_GET_MSR_INDEX_LIST, KVM_GET_MSRS, KVM_SET_MSRS) and does not require any special network path or authentication beyond the ability to interact with the KVM device.

Affected code

The vulnerability affects the KVM SVM module in arch/x86/kvm/svm/svm.c (svm_get_msr and svm_set_msr functions), arch/x86/kvm/svm/nested.c (svm_copy_vmrun_state function), and the common KVM x86 code in arch/x86/kvm/x86.c (msrs_to_save_base array) [patch_id=2660435].

What the fix does

The patch adds MSR_IA32_DEBUGCTLMSR and the four LBR MSRs to the msrs_to_save_base array in arch/x86/kvm/x86.c, so they are enumerated by KVM_GET_MSR_INDEX_LIST and included in save/restore [patch_id=2660435]. In arch/x86/kvm/svm/svm.c, new case handlers are added in svm_set_msr() for the four LBR MSRs, allowing writes only when LBR virtualization (lbrv) is enabled and only from host-initiated (userspace) context, since these are read-only MSRs from the guest's perspective. The svm_get_msr() handlers are also guarded with an lbrv check, returning 0 when LBR virtualization is disabled. In arch/x86/kvm/svm/nested.c, svm_copy_vmrun_state() is updated to copy LBR state (via svm_copy_lbrs()) and mask reserved bits in dbgctl when LBRV is available, ensuring L1's LBRs are correctly restored while L2 is running [patch_id=2660435].

Preconditions

  • configThe host must be running a KVM SVM (AMD) or VMX (Intel) hypervisor with LBR virtualization support.
  • authAn attacker must have the ability to issue KVM_GET_MSR_INDEX_LIST, KVM_GET_MSRS, or KVM_SET_MSRS ioctls (e.g., VM administrator access).
  • networkThe attacker interacts through the local KVM device interface; no network path is required.
  • inputThe attacker triggers save/restore or migration operations that involve LBR MSRs.

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.