[RFC PATCH v2 3/4] KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM

From: Binbin Wu

Date: Wed Jun 03 2026 - 22:31:32 EST


Reject unsupported TDX configurable CPUID bits provided by userspace
during KVM_TDX_INIT_VM.

While the TDX module allows the VMM to configure certain CPUID
features for a TD during initialization, KVM must strictly govern
which features are actually enabled. Allowing userspace to blindly
enable features that KVM does not yet support—particularly those
involving host state clobbering MSRs—could lead to host state
corruption, as KVM is not prepared to manage the associated
architectural state across host/guest transitions.

Replace the hardcoded denylist with a robust validation mechanism. By
leveraging the get_supported_cfg_cpuid() helper, KVM now explicitly
rejects the input if userspace attempts to set any TDX configurable bit
that is not present in KVM's allowlist.

Signed-off-by: Binbin Wu <binbin.wu@xxxxxxxxxxxxxxx>
---
arch/x86/kvm/vmx/tdx.c | 30 ++++++++++--------------------
1 file changed, 10 insertions(+), 20 deletions(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index e6bfec87a484..e44a862c6219 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -294,25 +294,6 @@ static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
}

-#define TDX_FEATURE_TSX (__feature_bit(X86_FEATURE_HLE) | __feature_bit(X86_FEATURE_RTM))
-
-static bool has_tsx(const struct kvm_cpuid_entry2 *entry)
-{
- return entry->function == 7 && entry->index == 0 &&
- (entry->ebx & TDX_FEATURE_TSX);
-}
-
-static bool has_waitpkg(const struct kvm_cpuid_entry2 *entry)
-{
- return entry->function == 7 && entry->index == 0 &&
- (entry->ecx & __feature_bit(X86_FEATURE_WAITPKG));
-}
-
-static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
-{
- return has_tsx(entry) || has_waitpkg(entry);
-}
-
static u32 get_supported_cfg_cpuid(u32 function, u32 index, u8 reg)
{
for (int i = 0; i < ARRAY_SIZE(tdx_kvm_supported_cpuid); i++) {
@@ -2526,6 +2507,15 @@ static int setup_tdparams_eptp_controls(struct kvm_cpuid2 *cpuid,
return 0;
}

+static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *e,
+ const struct kvm_cpuid_entry2 *mask)
+{
+ return ((e->eax & mask->eax & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EAX))) ||
+ (e->ebx & mask->ebx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EBX))) ||
+ (e->ecx & mask->ecx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_ECX))) ||
+ (e->edx & mask->edx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EDX))));
+}
+
static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
struct td_params *td_params)
{
@@ -2549,7 +2539,7 @@ static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
if (!entry)
continue;

- if (tdx_unsupported_cpuid(entry))
+ if (tdx_unsupported_cpuid(entry, &tmp))
return -EINVAL;

copy_cnt++;
--
2.46.0