Commit 2ee6983343 for qemu.org

commit 2ee698334373e9475b802f0b8163c52eeb1eaf77
Author: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date:   Thu Jul 2 14:53:09 2026 +0200

    target/i386/mshv: Fix cpu feature-leak from host

    In the current implementation there are subtle bugs that have to do with
    the termination of subleaves, resulting in features reported on the
    guest that it should not have according to the model.

    If a guest model has fewer valid subleaves stopping too early leaves
    unspecified gaps that are filled by the host, resulting in potentially
    invalid feature combinations, for example:

    The host reports features:

    CPUID[EAX=01H].ECX.AVX [bit 28] and
    CPUID[EAX=07H,ECX=01H].EAX.AVX_VNNI [bit 4]

    In the current implementation we would skip over 7.1 completely, not
    registering CPUID responses for the subleaf, resulting in a guest with
    qemu64 model (which does not feature AVX), reporting AVX_VNNI as
    available (passthrough from host).

    We need to walk through the max subleaves of leaf 7 and register them
    explicitly (also if they're 0).

    A second bug was about the propagation of "terminator" subleaves,
    all-zero responses that signal the end. We also want to register this
    terminator response , so the guest stops enumerating. In the current
    implementation we weren't doing that.

    Fixes: 4fa04dd1621
    Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
    Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>

diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 4f113da02d..f2b5641231 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1079,7 +1079,7 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
     CPUX86State *env = &x86_cpu->env;
     uint32_t eax, ebx, ecx, edx;
     uint32_t leaf, subleaf;
-    uint32_t max_basic_leaf, max_extended_leaf;
+    uint32_t max_basic_leaf, max_extended_leaf, max_subleaf_7;
     uint32_t max_subleaf = 0x20;
     uint32_t leaves_with_subleaves[] = {0x04, 0x07, 0x0d, 0x0f, 0x10};
     int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves);
@@ -1117,14 +1117,31 @@ static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries)
             continue;
         }

+        /*
+         * Valid subleaves for are reported in 7.0:EAX. We need to register all
+         * subleaves to the maximum subleaf, even if they are 0. Otherwise the
+         * host will supply its own values for a unregistered subleaf, which
+         * can result in an inconsistent feature set.
+         */
+        if (leaf == 0x07) {
+            cpu_x86_cpuid(env, leaf, 0, &max_subleaf_7, &ebx, &ecx, &edx);
+            for (subleaf = 0; subleaf <= max_subleaf_7; subleaf++) {
+                cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
+                add_cpuid_entry(cpuid_entries, leaf, subleaf,
+                                eax, ebx, ecx, edx);
+            }
+            continue;
+        }
+
         subleaf = 0;
         while (subleaf < max_subleaf) {
             cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);

+            /* register the "terminator" to the guest, before breaking */
+            add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx);
             if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
                 break;
             }
-            add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx);
             subleaf++;
         }
     }