Re: [PATCH v4 3/4] KVM: TDX: Filter configurable CPUID bits
From: Binbin Wu
Date: Tue Sep 22 2026 - 20:28:23 EST
On 9/23/2026 8:16 AM, Edgecombe, Rick P wrote:
> On Thu, 2026-09-17 at 15:25 +0800, Binbin Wu wrote:
>> Filter the directly configurable CPUID bits reported through
>> KVM_TDX_CAPABILITIES against KVM's TDX allowlist, and drop the hardcoded
>> denylist based filtering.
>>
>> The TDX module reports directly configurable CPUID bits that it supports
>> for a TD, but KVM must not expose bits that it doesn't support, as blindly
>> exposing a host state clobbering feature can lead to host state corruption.
>> The existing denylist, which clears only TSX and WAITPKG, is not fail-safe.
>>
>> Add tdx_get_cpuid_cfg_mask() to get the mask of directly configurable bits
>> allowed by KVM for a given CPUID register, covering both feature bits,
>> which come from tdx_cpu_cfg_caps[], and non-feature bits, which are
>> enumerated at runtime. Apply the mask to every CPUID entry reported
>> through KVM_TDX_CAPABILITIES.
>>
>> With the allowlist in place, newly introduced TDX directly configurable
>> CPUID bits stay hidden from userspace until KVM explicitly opts in.
>>
>> Note, filtering KVM_TDX_CAPABILITIES intentionally stops advertising the
>> directly configurable bits that aren't in the allowlist, as the
>> corresponding features are unsupported or cannot be properly virtualized.
>
> I think we need to more loudly call out that this is an ABI change and we expect
> it not to impact other VMMs because...
OK, will do this for this patch and patch 4.
Thanks!
>
>>
>> Update the documentation to reflect the ABI change.
>>
[...]
>> +#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)
>> +{
>> + /*
>> + * For a leaf/subleaf/register that will never be repurposed to hold
>> + * feature bits, it's safe to return TDX_CPUID_ALL_ALLOWED_MASK, i.e.
>> + * leave the TDX module's CPUID config mask intact.
>> + */
>> + switch (function) {
>> + case 1:
>> + if (reg == CPUID_EAX || reg == CPUID_EBX)
>> + return TDX_CPUID_ALL_ALLOWED_MASK;
>> + return 0;
>> + case 4:
>> + case 0x18:
>> + case 0x1f:
>> + return TDX_CPUID_ALL_ALLOWED_MASK;
>> + case 0x24:
>> + if (index == 0 && reg == CPUID_EBX)
>> + return GENMASK_U32(7, 0);
>> + return 0;
>> + case 0x80000008:
>> + if (reg == CPUID_EAX)
>> + return TDX_CPUID_ALL_ALLOWED_MASK;
>
> There are reserved bits in some of these. Are these "no feature bit" assumptions
> checked somehow?
Xiaoyao mentioned it in v3 too.
Sean suggested in v2 that "Realistically, CPUID.0x1.E{A,B}X are never going to be
repurposed to hold feature bits, and so generating a mask of allowed bits adds
unnecessary cognitive load and maintenance. Ditto for CPUID 0x4, 0x18, and 0x1F."
https://lore.kernel.org/kvm/aj1fi_0SBxMK5WOB@xxxxxxxxxx/
If we really have concerns, I can change it to exclude reserved bits in the next
version.