mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
KVM SVM changes for 7.4
- Fix a benign-in-practice bug where KVM unintentionally sets a non-zero
error code when delivering exceptions to SEV-ES+ guests.
- Force a new ASID in both VMCBs on pCPU migration to fix a bug where KVM can
run a VMCB with a stale ASID due to the tracking being per-CPU, not per-VMCB.
- Return INVALID_INPUT to the guest instead of exiting to userspace with -EIO
if KVM can't access "guest request" buffer, i.e. try to keep the guest alive
if the guest or userspace goofs.
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEKTobbabEP7vbhhN9OlYIJqCjN/0FAmrRnUsACgkQOlYIJqCj
N/3K6Q/9HzoeLCOZHfJ+QUuAG5KjlL28n95vinH+UcOPiM/DlMH2TqsE86sCAmWM
ldYuKeDftDYgCT6u6f9qejg4Cs2a+yC9LO13d/cUl47QYe33J4X2pvGyr4FAMv1Q
2Zm0U9XUFX44U0AS9eNT5orp+npIJTnFPCgS1f8ODiMJE7OydtKOdKpyiTZpc3Bf
g15EgjN+EOfVLUMtQpGaegGgGjeBNn1VcPW8QkkkBJE/BuAN3e9RIAKkucGHd2KH
v1vjj722brc+zC5xmhbtG03NsBxnEshYmgyoepllXmRr6Lp/yb0av+VUPimDazA5
q5bR0xVutyBWdO9pVTA1uLkVXZ3+KLKHyDedKATr4LBb4XJ7L3h/aAI0esPwRxiI
AYDYiz4hxD6qoHOE7vugTRHIu73TTfHN+/JhVBX6MyhBULuttnI8Zl/VAHal1foI
8oCCb5/DCXd854nDX2xwMvSIFrCjBGt+9LfKNnauG/4gjJrmWqFKdJ10vfZQrsPu
pukL8EWZHsywVFHfxLqON1XmYuz52Yp2bzXIf7M9MLddTAZMeRXGn8nOqOkbA+h2
+DLhDczU9iBodQ44tro87+qnmqnEHjRV9LJrLw5J6ahI5NV5oefifldrmpSAAG5A
0Q1wus5J+8Wy7sBDgvuGmsCvhi8k96cfTxJhAdd1NwD2XfWFFOo=
=MX9Z
-----END PGP SIGNATURE-----
Merge tag 'kvm-x86-svm-7.4' of https://github.com/kvm-x86/linux into HEAD
KVM SVM changes for 7.4
- Fix a benign-in-practice bug where KVM unintentionally sets a non-zero
error code when delivering exceptions to SEV-ES+ guests.
- Force a new ASID in both VMCBs on pCPU migration to fix a bug where KVM can
run a VMCB with a stale ASID due to the tracking being per-CPU, not per-VMCB.
- Return INVALID_INPUT to the guest instead of exiting to userspace with -EIO
if KVM can't access "guest request" buffer, i.e. try to keep the guest alive
if the guest or userspace goofs.
This commit is contained in:
commit
e43190558b
|
|
@ -641,8 +641,8 @@ static inline void __unused_size_checks(void)
|
|||
#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT)
|
||||
#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT)
|
||||
|
||||
#define SVM_EVTINJ_VALID (1 << 31)
|
||||
#define SVM_EVTINJ_VALID_ERR (1 << 11)
|
||||
#define SVM_EVTINJ_VALID BIT(31)
|
||||
#define SVM_EVTINJ_VALID_ERR BIT(11)
|
||||
|
||||
#define SVM_EVTINJ_RESERVED_BITS ~(SVM_EVTINJ_VEC_MASK | SVM_EVTINJ_TYPE_MASK | \
|
||||
SVM_EVTINJ_VALID_ERR | SVM_EVTINJ_VALID)
|
||||
|
|
|
|||
|
|
@ -1502,6 +1502,7 @@ int svm_allocate_nested(struct vcpu_svm *svm)
|
|||
if (!svm->nested.msrpm)
|
||||
goto err_free_vmcb02;
|
||||
|
||||
svm->nested.vmcb02.cpu = -1;
|
||||
svm->nested.initialized = true;
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -4239,6 +4239,7 @@ static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_
|
|||
struct kvm *kvm = svm->vcpu.kvm;
|
||||
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
|
||||
sev_ret_code fw_err = 0;
|
||||
u8 tickle = 0;
|
||||
int ret;
|
||||
|
||||
if (!is_sev_snp_guest(&svm->vcpu))
|
||||
|
|
@ -4246,8 +4247,21 @@ static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_
|
|||
|
||||
guard(mutex)(&sev->guest_req_mutex);
|
||||
|
||||
if (kvm_read_guest(kvm, req_gpa, sev->guest_req_buf, PAGE_SIZE))
|
||||
return -EIO;
|
||||
if (kvm_read_guest(kvm, req_gpa, sev->guest_req_buf, PAGE_SIZE)) {
|
||||
svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "Tickle" the response buffer to verify it's writable before sending
|
||||
* the request to firmware, which will modify the VMPCK sequence number.
|
||||
* Note, this is a best effort check and doesn't guard against TOCTOU
|
||||
* issues. See below for more information.
|
||||
*/
|
||||
if (kvm_write_guest(kvm, resp_gpa, &tickle, sizeof(tickle))) {
|
||||
svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
data.gctx_paddr = __psp_pa(sev->snp_context);
|
||||
data.req_paddr = __psp_pa(sev->guest_req_buf);
|
||||
|
|
@ -4262,6 +4276,13 @@ static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_
|
|||
if (ret && !fw_err)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Exit to userspace if writing the response fails, e.g. if the mapping
|
||||
* changed between the initial tickle and the actual write, to avoid
|
||||
* creating an ambiguous failure ABI with the guest. Don't return an
|
||||
* error to the guest because firmware already incremented the VMPCK
|
||||
* sequence number, but there's no way to communicate that to the guest.
|
||||
*/
|
||||
if (kvm_write_guest(kvm, resp_gpa, sev->guest_resp_buf, PAGE_SIZE))
|
||||
return -EIO;
|
||||
|
||||
|
|
@ -4313,8 +4334,10 @@ static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r
|
|||
return -EINVAL;
|
||||
|
||||
if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type),
|
||||
&msg_type, 1))
|
||||
return -EIO;
|
||||
&msg_type, 1)) {
|
||||
svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per GHCB spec, requests of type MSG_REPORT_REQ also allow for
|
||||
|
|
@ -4353,8 +4376,10 @@ static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r
|
|||
* As per GHCB spec (see "SNP Extended Guest Request"), the
|
||||
* certificate table is terminated by 24-bytes of zeroes.
|
||||
*/
|
||||
if (data_npages && kvm_clear_guest(kvm, data_gpa, 24))
|
||||
return -EIO;
|
||||
if (data_npages && kvm_clear_guest(kvm, data_gpa, 24)) {
|
||||
svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return snp_handle_guest_req(svm, req_gpa, resp_gpa);
|
||||
|
|
|
|||
|
|
@ -3875,14 +3875,25 @@ static int pre_svm_run(struct kvm_vcpu *vcpu)
|
|||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
/*
|
||||
* If the previous vmrun of the vmcb occurred on a different physical
|
||||
* cpu, then mark the vmcb dirty and assign a new asid. Hardware's
|
||||
* vmcb clean bits are per logical CPU, as are KVM's asid assignments.
|
||||
* If the previous VMRUN of the VMCB occurred on a different physical
|
||||
* cpu, then mark the VMCB dirty as hardware's clean bits are per pCPU.
|
||||
*
|
||||
* Reset the ASID generation in both VMCBs. This will lead to assigning
|
||||
* a new ASID now, and then again when switching to the other VMCB.
|
||||
* However, this is needed as the ASID is shared between the VMCBs, and
|
||||
* otherwise it would be possible to use an ASID allocated on one pCPU
|
||||
* on another, for example:
|
||||
* - vCPU migrates from pCPU A to pCPU B, allocates a new ASID.
|
||||
* - vCPU migrates back to pCPU A, and then switches the VMCB.
|
||||
* - The new VMCB does not detect a pCPU change and runs on pCPU A with
|
||||
* the new ASID allocated on pCPU B, which is potentially used by
|
||||
* another vCPU/VM.
|
||||
*/
|
||||
if (unlikely(svm->current_vmcb->cpu != vcpu->cpu)) {
|
||||
svm->current_vmcb->asid_generation = 0;
|
||||
vmcb_mark_all_dirty(svm->vmcb);
|
||||
svm->current_vmcb->cpu = vcpu->cpu;
|
||||
svm->vmcb01.asid_generation = 0;
|
||||
svm->nested.vmcb02.asid_generation = 0;
|
||||
}
|
||||
|
||||
if (is_sev_guest(vcpu))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user