[PATCH v4 27/28] KVM: TDX: Bug the VM if extending the initial measurement fails

Binbin Wu binbin.wu at linux.intel.com
Tue Nov 4 17:16:53 AEDT 2025



On 10/31/2025 4:09 AM, Sean Christopherson wrote:
> WARN and terminate the VM if TDH_MR_EXTEND fails, as extending the
> measurement should fail if and only if there is a KVM bug, or if the S-EPT
> mapping is invalid.  Now that KVM makes all state transitions mutually
> exclusive via tdx_vm_state_guard, it should be impossible for S-EPT
> mappings to be removed between kvm_tdp_mmu_map_private_pfn() and
> tdh_mr_extend().
>
> Holding slots_lock prevents zaps due to memslot updates,
> filemap_invalidate_lock() prevents zaps due to guest_memfd PUNCH_HOLE,
> vcpu->mutex locks prevents updates from other vCPUs, kvm->lock prevents
> VM-scoped ioctls from creating havoc (e.g. by creating new vCPUs), and all
> usage of kvm_zap_gfn_range() is mutually exclusive with S-EPT entries that
> can be used for the initial image.
>
> For kvm_zap_gfn_range(), the call from sev.c is obviously mutually
> exclusive, TDX disallows KVM_X86_QUIRK_IGNORE_GUEST_PAT so the same goes
> for kvm_noncoherent_dma_assignment_start_or_stop(), and
> __kvm_set_or_clear_apicv_inhibit() is blocked by virtue of holding all
> VM and vCPU mutexes (and the APIC page has its own non-guest_memfd memslot

Nit:
It sounds like TDX is using the memslot for the APIC page, but for a TD, the
memslot for the APIC page is never initialized or used?

> and so can't be used for the initial image, which means that too is
> mutually exclusive irrespective of locking).
>
> Opportunistically return early if the region doesn't need to be measured
> in order to reduce line lengths and avoid wraps.  Similarly, immediately
> and explicitly return if TDH_MR_EXTEND fails to make it clear that KVM
> needs to bail entirely if extending the measurement fails.
>
> Signed-off-by: Sean Christopherson <seanjc at google.com>

Reviewed-by: Binbin Wu <binbin.wu at linux.intel.com>

> ---
>   arch/x86/kvm/vmx/tdx.c | 24 +++++++++++++-----------
>   1 file changed, 13 insertions(+), 11 deletions(-)
>
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index 8bcdec049ac6..762f2896547f 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
> @@ -3123,21 +3123,23 @@ static int tdx_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
>   
>   	put_page(src_page);
>   
> -	if (ret)
> +	if (ret || !(arg->flags & KVM_TDX_MEASURE_MEMORY_REGION))
>   		return ret;
>   
> -	if (arg->flags & KVM_TDX_MEASURE_MEMORY_REGION) {
> -		for (i = 0; i < PAGE_SIZE; i += TDX_EXTENDMR_CHUNKSIZE) {
> -			err = tdh_mr_extend(&kvm_tdx->td, gpa + i, &entry,
> -					    &level_state);
> -			if (err) {
> -				ret = -EIO;
> -				break;
> -			}
> -		}
> +	/*
> +	 * Note, MR.EXTEND can fail if the S-EPT mapping is somehow removed
> +	 * between mapping the pfn and now, but slots_lock prevents memslot
> +	 * updates, filemap_invalidate_lock() prevents guest_memfd updates,
> +	 * mmu_notifier events can't reach S-EPT entries, and KVM's internal
> +	 * zapping flows are mutually exclusive with S-EPT mappings.
> +	 */
> +	for (i = 0; i < PAGE_SIZE; i += TDX_EXTENDMR_CHUNKSIZE) {
> +		err = tdh_mr_extend(&kvm_tdx->td, gpa + i, &entry, &level_state);
> +		if (TDX_BUG_ON_2(err, TDH_MR_EXTEND, entry, level_state, kvm))
> +			return -EIO;
>   	}
>   
> -	return ret;
> +	return 0;
>   }
>   
>   static int tdx_vcpu_init_mem_region(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd)



More information about the Linuxppc-dev mailing list