]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: SEV: Decouple the need to sync the GHCB SA from the need to free the SA
authorSean Christopherson <seanjc@google.com>
Fri, 29 May 2026 18:35:41 +0000 (20:35 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 3 Jun 2026 14:43:56 +0000 (16:43 +0200)
Decouple synchronizing the GHCB SA from freeing/unpinning the SA, so that
the free/unpin path can be reused when freeing a vCPU.

Opportunistically add a WARN to harden KVM against stomping over (and thus
leaking) an already-allocated scratch area.

Cc: stable@vger.kernel.org
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20260501202250.2115252-17-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-ID: <20260529183549.1104619-17-pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/svm/sev.c

index 437282f0ea9438fceeccb754239c2b3419fd9818..11d46600cbdc0bf2459ae7e9be14153138920e40 100644 (file)
@@ -3560,20 +3560,17 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm)
        if (!svm->sev_es.ghcb)
                return;
 
-       if (svm->sev_es.ghcb_sa_free) {
-               /*
-                * The scratch area lives outside the GHCB, so there is a
-                * buffer that, depending on the operation performed, may
-                * need to be synced, then freed.
-                */
-               if (svm->sev_es.ghcb_sa_sync) {
-                       kvm_write_guest(svm->vcpu.kvm,
-                                       svm->sev_es.sw_scratch,
-                                       svm->sev_es.ghcb_sa,
-                                       svm->sev_es.ghcb_sa_len);
-                       svm->sev_es.ghcb_sa_sync = false;
-               }
+       /*
+        * If the scratch area lives outside the GHCB, there's a buffer that,
+        * depending on the operation performed, may need to be synced.
+        */
+       if (svm->sev_es.ghcb_sa_sync) {
+               kvm_write_guest(svm->vcpu.kvm, svm->sev_es.sw_scratch,
+                               svm->sev_es.ghcb_sa, svm->sev_es.ghcb_sa_len);
+               svm->sev_es.ghcb_sa_sync = false;
+       }
 
+       if (svm->sev_es.ghcb_sa_free) {
                kvfree(svm->sev_es.ghcb_sa);
                svm->sev_es.ghcb_sa = NULL;
                svm->sev_es.ghcb_sa_free = false;
@@ -3685,6 +3682,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len)
                goto e_scratch;
        }
 
+       WARN_ON_ONCE(svm->sev_es.ghcb_sa_sync || svm->sev_es.ghcb_sa_free);
+
        if ((scratch_gpa_beg & PAGE_MASK) == control->ghcb_gpa) {
                /* Scratch area begins within GHCB */
                ghcb_scratch_beg = control->ghcb_gpa +
@@ -3706,6 +3705,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len)
                scratch_va = (void *)svm->sev_es.ghcb;
                scratch_va += (scratch_gpa_beg - control->ghcb_gpa);
 
+               svm->sev_es.ghcb_sa_sync = false;
+               svm->sev_es.ghcb_sa_free = false;
                svm->sev_es.ghcb_sa_len = ghcb_scratch_end - scratch_gpa_beg;
        } else {
                /* GHCB v2 requires the scratch area to be within the GHCB. */