mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Binbin Wu <binbin.wu@linux.intel.com>
To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Cc: seanjc@google.com, pbonzini@redhat.com,
	dave.hansen@linux.intel.com, andrew.cooper3@citrix.com,
	nik.borisov@suse.com, kas@kernel.org, rick.p.edgecombe@intel.com,
	xiaoyao.li@intel.com, chao.gao@intel.com,
	tony.lindgren@linux.intel.com, kishen.maloor@intel.com,
	dedekind1@gmail.com, binbin.wu@linux.intel.com
Subject: [PATCH v4 4/4] KVM: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
Date: Thu, 17 Sep 2026 15:25:48 +0800	[thread overview]
Message-ID: <20260917072548.2314491-5-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20260917072548.2314491-1-binbin.wu@linux.intel.com>

Validate the CPUID configuration provided by userspace through
KVM_TDX_INIT_VM against KVM's TDX allowlist, and drop the hardcoded
denylist based check.

The TDX module lets the VMM configure certain CPUID features for a TD at
initialization time, but KVM must strictly govern which of them userspace
can actually enable, otherwise a host state clobbering feature could be
enabled behind KVM's back.  The existing check only rejects TSX and
WAITPKG, i.e. it is not fail-safe, as any bit that a future TDX module
makes configurable would be accepted even if KVM has no idea about the
feature.

Add tdx_has_unsupported_cpuid_cfg_bit() and reject KVM_TDX_INIT_VM if
userspace sets any bit outside the mask returned by
tdx_get_cpuid_cfg_mask().  There is no need to first mask the userspace
input with the bits the TDX module reports as directly configurable, as
anything outside that set is rejected by the TDX module itself.

Also reject CPUID entries whose index differs from the value expected by
the TDX module, as kvm_find_cpuid_entry2() ignores the index when
KVM_CPUID_FLAG_SIGNIFCANT_INDEX is cleared, i.e. a mismatching entry could
otherwise be applied to the wrong subleaf.

Update the comments for KVM_TDX_INIT_VM in the uapi header and the TDX
documentation accordingly.

Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
---
v4:
- Update the comments/documentation.
- Collect RB tag from Tony.

v3:
- Check CPUID entry index mismatch b/t userspace input and TDX sysinfo
  configuration. (Sashiko)
- No need to mask the userspace input with TDX module reported directly
  configurable bits first, since the userspace input should be subset of
  directly configurable bits. Otherwise, the input will be rejected by
  the TDX module.
---
 Documentation/virt/kvm/x86/intel-tdx.rst |  2 ++
 arch/x86/include/uapi/asm/kvm.h          |  2 ++
 arch/x86/kvm/vmx/tdx.c                   | 41 ++++++++++++------------
 3 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/Documentation/virt/kvm/x86/intel-tdx.rst b/Documentation/virt/kvm/x86/intel-tdx.rst
index 6beeb89d7f057..36bd0fee2480d 100644
--- a/Documentation/virt/kvm/x86/intel-tdx.rst
+++ b/Documentation/virt/kvm/x86/intel-tdx.rst
@@ -135,6 +135,8 @@ KVM_CREATE_VM and before creating any VCPUs.
         /*
          * Call KVM_TDX_INIT_VM before vcpu creation, thus before
          * KVM_SET_CPUID2.
+         * KVM validates @cpuid, i.e. setting a bit that KVM_TDX_CAPABILITIES
+         * doesn't report as configurable fails the ioctl.
          * This configuration supersedes KVM_SET_CPUID2s for VCPUs because the
          * TDX module directly virtualizes those CPUIDs without VMM.  The user
          * space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 1585ec8040666..784cb5cd7f664 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -1032,6 +1032,8 @@ struct kvm_tdx_init_vm {
 	/*
 	 * Call KVM_TDX_INIT_VM before vcpu creation, thus before
 	 * KVM_SET_CPUID2.
+	 * KVM validates @cpuid, i.e. setting a bit that KVM_TDX_CAPABILITIES
+	 * doesn't report as configurable fails the ioctl.
 	 * This configuration supersedes KVM_SET_CPUID2s for VCPUs because the
 	 * TDX module directly virtualizes those CPUIDs without VMM.  The user
 	 * space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index ad7d70b36ffe5..5426191905d0d 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -286,25 +286,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);
-}
-
 #define TDX_CPUID_ALL_ALLOWED_MASK	GENMASK_U32(31, 0)
 
 static u32 tdx_get_cpuid_cfg_non_feature_mask(u32 function, u32 index, int reg)
@@ -2548,6 +2529,17 @@ static int setup_tdparams_eptp_controls(struct kvm_cpuid2 *cpuid,
 	return 0;
 }
 
+static bool tdx_has_unsupported_cpuid_cfg_bit(const struct kvm_cpuid_entry2 *entry)
+{
+	u32 function = entry->function;
+	u32 index = entry->index;
+
+	return (entry->eax & ~tdx_get_cpuid_cfg_mask(function, index, CPUID_EAX)) ||
+	       (entry->ebx & ~tdx_get_cpuid_cfg_mask(function, index, CPUID_EBX)) ||
+	       (entry->ecx & ~tdx_get_cpuid_cfg_mask(function, index, CPUID_ECX)) ||
+	       (entry->edx & ~tdx_get_cpuid_cfg_mask(function, index, CPUID_EDX));
+}
+
 static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
 				 struct td_params *td_params)
 {
@@ -2571,7 +2563,16 @@ static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
 		if (!entry)
 			continue;
 
-		if (tdx_unsupported_cpuid(entry))
+		/*
+		 * Reject entries whose index doesn't match the expected one.
+		 * This catches userspace passing a CPUID entry with the
+		 * KVM_CPUID_FLAG_SIGNIFCANT_INDEX flag cleared when the index
+		 * is significant.
+		 */
+		if (entry->index != tmp.index)
+			return -EINVAL;
+
+		if (tdx_has_unsupported_cpuid_cfg_bit(entry))
 			return -EINVAL;
 
 		copy_cnt++;
-- 
2.46.0


      parent reply	other threads:[~2026-09-17  7:22 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-17  7:25 [PATCH v4 0/4] KVM: TDX: Validate directly configurable CPUID bits Binbin Wu
2026-09-17  7:25 ` [PATCH v4 1/4] KVM: TDX: Track configurable CPUID bits allowed by KVM Binbin Wu
2026-09-17  7:25 ` [PATCH v4 2/4] KVM: TDX: Report CORE_CAPABILITIES as configurable Binbin Wu
2026-09-17  7:25 ` [PATCH v4 3/4] KVM: TDX: Filter configurable CPUID bits Binbin Wu
2026-09-17  7:25 ` Binbin Wu [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260917072548.2314491-5-binbin.wu@linux.intel.com \
    --to=binbin.wu@linux.intel.com \
    --cc=andrew.cooper3@citrix.com \
    --cc=chao.gao@intel.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=dedekind1@gmail.com \
    --cc=kas@kernel.org \
    --cc=kishen.maloor@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nik.borisov@suse.com \
    --cc=pbonzini@redhat.com \
    --cc=rick.p.edgecombe@intel.com \
    --cc=seanjc@google.com \
    --cc=tony.lindgren@linux.intel.com \
    --cc=xiaoyao.li@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®