From: Binbin Wu <binbin.wu@linux.intel.com>
To: kvm@vger.kernel.org
Cc: pbonzini@redhat.com, seanjc@google.com,
rick.p.edgecombe@intel.com, xiaoyao.li@intel.com,
chao.gao@intel.com, kai.huang@intel.com,
binbin.wu@linux.intel.com
Subject: [RFC PATCH 22/27] KVM: x86: Verify userspace CPUID inputs in paranoid mode
Date: Fri, 17 Apr 2026 15:36:05 +0800 [thread overview]
Message-ID: <20260417073610.3246316-23-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20260417073610.3246316-1-binbin.wu@linux.intel.com>
Add CPUID paranoid verification to reject userspace CPUID configurations
that set unsupported or unknown bits when paranoid mode is enabled for a
VM.
When paranoid mode is enabled, iterate over every userspace-provided
CPUID entry and check all four registers (EAX-EDX) against KVM's
supported masks or values.
Introduce cpuid_reg_2_x86_leaf() to reverse-map a (function, index, reg)
tuple to a reverse_cpuid[] index, handling subleaf remapping for CPUID
leaves with a common pattern across sub-leaves.
Refactor the vCPU capability initialization to iterate over userspace
CPUID entries rather than reverse_cpuid[], combining the paranoid check
with capability setup in cpuid_check_and_set_vcpu_caps(). When paranoid
mode is disabled, entries without a reverse_cpuid[] mapping are simply
skipped, preserving existing behavior.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
arch/x86/kvm/cpuid.c | 142 ++++++++++++++++++++++++++++++++++---------
1 file changed, 113 insertions(+), 29 deletions(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 08f5bc1d26b1..2027230a1f42 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -406,7 +406,7 @@ static void kvm_cpu_cap_ignore(u32 func, u32 index_start, u32 index_end,
ignored_set.nr++;
}
-static bool __maybe_unused is_cpuid_paranoid_ignored(u32 func, u32 index, int reg, u8 overlay)
+static bool is_cpuid_paranoid_ignored(u32 func, u32 index, int reg, u8 overlay)
{
for (int i = 0; i < ignored_set.nr; i++) {
struct ignored_entry *e = &ignored_set.entries[i];
@@ -419,7 +419,7 @@ static bool __maybe_unused is_cpuid_paranoid_ignored(u32 func, u32 index, int re
return false;
}
-static bool __maybe_unused is_cpuid_reg_check_value(u32 func, u32 index, int reg)
+static bool is_cpuid_reg_check_value(u32 func, u32 index, int reg)
{
switch (func) {
case 0x1D: return true;
@@ -428,7 +428,7 @@ static bool __maybe_unused is_cpuid_reg_check_value(u32 func, u32 index, int reg
}
}
-static bool __maybe_unused is_cpuid_subleaf_common_pattern(u32 func, u32 *index)
+static bool is_cpuid_subleaf_common_pattern(u32 func, u32 *index)
{
switch (func) {
case 4:
@@ -449,45 +449,129 @@ static bool __maybe_unused is_cpuid_subleaf_common_pattern(u32 func, u32 *index)
}
}
-int kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
+static u32 cpuid_reg_2_x86_leaf(u32 leaf, u32 index, int reg)
{
- u8 cpuid_overlay = get_cpuid_overlay(vcpu->kvm);
- struct kvm_lapic *apic = vcpu->arch.apic;
- struct kvm_cpuid_entry2 *best;
- struct kvm_cpuid_entry2 *entry;
- bool allow_gbpages;
- int i;
+ bool remapped = false;
- memset(vcpu->arch.cpu_caps, 0, sizeof(vcpu->arch.cpu_caps));
- BUILD_BUG_ON(ARRAY_SIZE(reverse_cpuid) != NR_KVM_CPU_CAPS_PARANOID);
+ if (is_cpuid_subleaf_common_pattern(leaf, &index))
+ remapped = true;
- /*
- * Reset guest capabilities to userspace's guest CPUID definition, i.e.
- * honor userspace's definition for features that don't require KVM or
- * hardware management/support (or that KVM simply doesn't care about).
- */
- for (i = 0; i < NR_KVM_CPU_CAPS; i++) {
- const struct cpuid_reg cpuid = reverse_cpuid[i];
- struct kvm_cpuid_entry2 emulated;
+ for (int i = 0; i < ARRAY_SIZE(reverse_cpuid); i++) {
+ const struct cpuid_reg *cpuid = &reverse_cpuid[i];
+
+ if (cpuid->function == leaf && cpuid->index == index && cpuid->reg == reg) {
+ /*
+ * Remapping index is only inteded for paranoid CPUID
+ * checks, it should not fall into the range, which
+ * is tracked by vCPU's caps.
+ */
+ WARN_ON_ONCE(remapped && i < NR_KVM_CPU_CAPS);
+ return i;
+ }
+ }
- if (!cpuid.function)
+ return (u32)-1;
+}
+
+static int do_cpuid_reg_paranoid_check(struct kvm *kvm,
+ struct kvm_cpuid_entry2 *entry, int reg,
+ u32 input, u32 supported)
+{
+ bool check_value;
+
+ if (!kvm->arch.is_cpuid_paranoid_mode)
+ return 0;
+
+ check_value = is_cpuid_reg_check_value(entry->function, entry->index, reg);
+
+ if (check_value && (input == supported))
+ return 0;
+
+ if (!check_value && (input & supported) == input)
+ return 0;
+
+ pr_debug("CPUID func 0x%x index %d E%cX: 0x%08x %s 0x%08x\n",
+ entry->function, entry->index, 'A' + reg, input,
+ check_value ? "!=" : "has unsupported bits",
+ check_value ? supported : input & ~supported);
+
+ return -EINVAL;
+}
+
+static int cpuid_check_and_set_vcpu_caps(struct kvm_vcpu *vcpu,
+ struct kvm_cpuid_entry2 *entry)
+{
+ u8 cpuid_overlay = get_cpuid_overlay(vcpu->kvm);
+ struct kvm_cpuid_entry2 emulated;
+ u32 input, supported;
+ u32 leaf;
+
+ if (!entry->index)
+ cpuid_func_emulated(vcpu->kvm, &emulated, entry->function, true);
+
+ for (int reg = CPUID_EAX; reg <= CPUID_EDX; reg++) {
+ if (vcpu->kvm->arch.is_cpuid_paranoid_mode &&
+ is_cpuid_paranoid_ignored(entry->function, entry->index, reg, cpuid_overlay))
continue;
- entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);
- if (!entry)
+ /*
+ * For a leaf remapped from a different index, it will
+ * not be set to vcpu->arch.cpu_caps[] below.
+ */
+ leaf = cpuid_reg_2_x86_leaf(entry->function, entry->index, reg);
+
+ if (!vcpu->kvm->arch.is_cpuid_paranoid_mode && leaf >= NR_KVM_CPU_CAPS)
continue;
+ input = cpuid_get_reg_unsafe(entry, reg);
+
+ supported = leaf != (u32)-1 ? kvm_cpu_caps[cpuid_overlay][leaf] : 0;
+ supported |= (!entry->index ? cpuid_get_reg_unsafe(&emulated, reg) : 0);
+
+ if (do_cpuid_reg_paranoid_check(vcpu->kvm, entry, reg, input, supported))
+ return -EINVAL;
+
+ if (leaf >= NR_KVM_CPU_CAPS)
+ continue;
/*
* A vCPU has a feature if it's supported by KVM and is enabled
* in guest CPUID. Note, this includes features that are
* supported by KVM but aren't advertised to userspace!
*/
- vcpu->arch.cpu_caps[i] = kvm_cpu_caps[cpuid_overlay][i];
- if (!cpuid.index) {
- cpuid_func_emulated(vcpu->kvm, &emulated, cpuid.function, true);
- vcpu->arch.cpu_caps[i] |= cpuid_get_reg_unsafe(&emulated, cpuid.reg);
- }
- vcpu->arch.cpu_caps[i] &= cpuid_get_reg_unsafe(entry, cpuid.reg);
+ vcpu->arch.cpu_caps[leaf] = supported;
+ vcpu->arch.cpu_caps[leaf] &= cpuid_get_reg_unsafe(entry, reg);
+ }
+
+ return 0;
+}
+
+int kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+ struct kvm_cpuid_entry2 *best;
+ bool allow_gbpages;
+ int r = 0;
+
+ memset(vcpu->arch.cpu_caps, 0, sizeof(vcpu->arch.cpu_caps));
+ BUILD_BUG_ON(ARRAY_SIZE(reverse_cpuid) != NR_KVM_CPU_CAPS_PARANOID);
+
+ /*
+ * If CPUID paranoid mode is enabled, KVM rejects userspace's guest
+ * CPUID definition if it contains any bits that aren't supported or
+ * unknown by KVM. Otherwise, reset guest capabilities to userspace's
+ * guest CPUID definition, i.e. honor userspace's definition for
+ * features that don't require KVM or hardware management/support (or
+ * that KVM simply doesn't care about).
+ */
+ for (int i = 0; i < vcpu->arch.cpuid_nent; i++) {
+ r = cpuid_check_and_set_vcpu_caps(vcpu, &vcpu->arch.cpuid_entries[i]);
+ /*
+ * No need to worry about the changes having been made if any
+ * check fails, all the changes will be reverted when returning
+ * an error on the set CPUID patch.
+ */
+ if (r)
+ return r;
}
kvm_update_cpuid_runtime(vcpu);
--
2.46.0
next prev parent reply other threads:[~2026-04-17 7:32 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-17 7:35 [RFC PATCH 00/27] KVM: x86: Add a paranoid mode for CPUID verification Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 01/27] KVM: x86: Fix emulated CPUID features being applied to wrong sub-leaf Binbin Wu
2026-05-15 9:03 ` Xiaoyao Li
2026-04-17 7:35 ` [RFC PATCH 02/27] KVM: x86: Reorder the features for CPUID 7 Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 03/27] KVM: x86: Add definitions for CPUID overlays Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 04/27] KVM: x86: Extend F() and its variants " Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 05/27] KVM: x86: Extend kvm_cpu_cap_{set/clear}() to configure overlays Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 06/27] KVM: x86: Populate TDX CPUID overlay with supported feature bits Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 07/27] KVM: x86: Support KVM_GET_{SUPPORTED,EMULATED}_CPUID as VM scope ioctls Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 08/27] KVM: x86: Thread @kvm to KVM CPU capability helpers Binbin Wu
2026-04-21 6:18 ` Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 09/27] KVM: x86: Use overlays of KVM CPU capabilities Binbin Wu
2026-04-21 5:31 ` Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 10/27] KVM: x86: Use vendor-specific overlay flags instead of F_CPUID_DEFAULT Binbin Wu
2026-04-21 6:43 ` Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 11/27] KVM: SVM: Drop unnecessary clears of unsupported common x86 features Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 12/27] KVM: x86: Split KVM CPU cap leafs into two parts Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 13/27] KVM: x86: Add a helper to initialize CPUID multi-bit fields Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 14/27] KVM: x86: Add a helper to init multiple feature bits based on raw CPUID Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 15/27] KVM: x86: Add infrastructure to track CPUID entries ignored in paranoid mode Binbin Wu
2026-04-17 7:35 ` [RFC PATCH 16/27] KVM: x86: Init allowed masks for basic CPUID range " Binbin Wu
2026-04-21 6:51 ` Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 17/27] KVM: x86: Init allowed masks for extended " Binbin Wu
2026-04-21 7:55 ` Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 18/27] KVM: x86: Handle Centaur CPUID leafs " Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 19/27] KVM: x86: Track KVM PV CPUID features for " Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 20/27] KVM: x86: Add per-VM flag to track CPUID " Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 21/27] KVM: x86: Make kvm_vcpu_after_set_cpuid() return an error code Binbin Wu
2026-04-22 8:22 ` Binbin Wu
2026-04-17 7:36 ` Binbin Wu [this message]
2026-04-22 8:59 ` [RFC PATCH 22/27] KVM: x86: Verify userspace CPUID inputs in paranoid mode Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 23/27] KVM: x86: Account for runtime CPUID features " Binbin Wu
2026-04-23 2:41 ` Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 24/27] KVM: x86: Skip paranoid CPUID check for KVM PV leafs when base is relocated Binbin Wu
2026-04-23 3:02 ` Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 25/27] KVM: x86: Add new KVM_CAP_X86_CPUID_PARANOID Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 26/27] KVM: x86: Add a helper to query the allowed CPUID mask Binbin Wu
2026-04-17 7:36 ` [RFC PATCH 27/27] KVM: TDX: Replace hardcoded CPUID filtering with the allowed mask Binbin Wu
2026-04-23 3:25 ` Binbin Wu
2026-05-15 8:08 ` [RFC PATCH 00/27] KVM: x86: Add a paranoid mode for CPUID verification Xiaoyao Li
2026-05-15 15:45 ` Edgecombe, Rick P
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=20260417073610.3246316-23-binbin.wu@linux.intel.com \
--to=binbin.wu@linux.intel.com \
--cc=chao.gao@intel.com \
--cc=kai.huang@intel.com \
--cc=kvm@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=seanjc@google.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.