All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: x86: bound the composed nested TSC multiplier to the max ratio
@ 2026-07-22  8:47 amirmohammad.eftekhar
  2026-07-22  8:47 ` [PATCH] KVM: SVM: use wrmsrq_safe() when writing the nested TSC ratio MSR amirmohammad.eftekhar
  2026-07-22  9:16 ` [PATCH] KVM: x86: bound the composed nested TSC multiplier to the max ratio sashiko-bot
  0 siblings, 2 replies; 5+ messages in thread
From: amirmohammad.eftekhar @ 2026-07-22  8:47 UTC (permalink / raw)
  To: kvm; +Cc: Sean Christopherson, Paolo Bonzini, rossow, Amirmohammad Eftekhar

From: Amirmohammad Eftekhar <amirmohammad.eftekhar@cispa.de>

kvm_calc_nested_tsc_multiplier() folds L1's own TSC multiplier (M1) with
the multiplier L1 requests for L2 (M2) into the single hardware ratio
programmed for L2: M_eff = (M1 * M2) >> frac.  Each input is validated
individually against the per-vendor maximum, but the composed output is
not, and it is stored into a 64-bit field:

 - Intel (frac = 48): when M1 * M2 >= 2^(64+48) the true M_eff exceeds
   2^64 and the low 64 bits are programmed silently.  L2 then runs at a
   wrong rate; the truncated value can even land on default_tsc_scaling_
   ratio (2^48), disabling scaling entirely, or on 0, which fails the
   nested VM-entry.  This is reachable with no adversary on live
   migration: M1 = (guest_khz << 48) / host_khz grows on a slower
   destination, M_eff is recomposed on KVM_SET_NESTED_STATE / the
   destination KVM_SET_TSC_KHZ with the same missing check, and a
   (M1, M2) legal on the source can recompose to M_eff == 0 on the
   destination, whose mid-flight resume fails hardware VM-entry on
   vmcs02 and tears the guest down (KVM_EXIT_SHUTDOWN).

 - AMD (frac = 32): the ratio MSR is 40 bits, but M_eff can reach ~2^48,
   setting reserved bits that architecturally #GP the physical
   MSR_AMD64_TSC_RATIO write (see the companion wrmsrq_safe() change).

KVM already rejects an input M2 == 0 at nested-entry validation; extend
that to the composed result.  Compute the full 128-bit product, detect a
value that does not fit the field (its high 64 bits reach 1 << frac) or
exceeds kvm_caps.max_tsc_scaling_ratio, and saturate to the advertised
maximum -- keeping the guest alive at a representable ratio instead of
truncating.  This runs on both the entry compose (prepare_vmcs02 /
nested_svm_update_tsc_ratio_msr) and the restore/recompute paths that
share this helper (kvm_vcpu_write_tsc_multiplier, KVM_SET_NESTED_STATE).

Signed-off-by: Amirmohammad Eftekhar <amirmohammad.eftekhar@cispa.de>
---
This and the follow-up wrmsrq_safe() patch (next in this thread) are
sent together to security@ rather than the public list: the AMD side
is why -- the composed value here can carry reserved bits straight into
a physical MSR write in host ring 0, and today only the WRMSR exception
table stands between that and a real #GP oops on a build that lacks it.

 arch/x86/kvm/x86.c | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0ce0d30e8825..5856b2358ee2 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2676,11 +2676,31 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_calc_nested_tsc_offset);
 
 u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier)
 {
-	if (l2_multiplier != kvm_caps.default_tsc_scaling_ratio)
-		return mul_u64_u64_shr(l1_multiplier, l2_multiplier,
-				       kvm_caps.tsc_scaling_ratio_frac_bits);
+	unsigned int frac = kvm_caps.tsc_scaling_ratio_frac_bits;
+	u64 nested_multiplier;
 
-	return l1_multiplier;
+	if (l2_multiplier == kvm_caps.default_tsc_scaling_ratio)
+		return l1_multiplier;
+
+	/*
+	 * Both inputs are individually within [1, max_tsc_scaling_ratio], but
+	 * their fixed-point product (M1 * M2) >> frac can exceed the width of
+	 * the hardware TSC multiplier field: on Intel the high bits would be
+	 * silently truncated (yielding a wrong -- or even zero -- L2 scaling
+	 * ratio), and on AMD the surplus bits are reserved bits that #GP the
+	 * physical MSR_AMD64_TSC_RATIO write.  (M1 * M2) >> frac overflows u64
+	 * iff the high 64 bits of the 128-bit product are >= (1 << frac);
+	 * detect that (and any result above the advertised maximum) and
+	 * saturate rather than program an unrepresentable ratio.
+	 */
+	if (mul_u64_u64_shr(l1_multiplier, l2_multiplier, 64) >= (1ULL << frac))
+		return kvm_caps.max_tsc_scaling_ratio;
+
+	nested_multiplier = mul_u64_u64_shr(l1_multiplier, l2_multiplier, frac);
+	if (nested_multiplier > kvm_caps.max_tsc_scaling_ratio)
+		return kvm_caps.max_tsc_scaling_ratio;
+
+	return nested_multiplier;
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_calc_nested_tsc_multiplier);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-22 13:51 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-22  8:47 [PATCH] KVM: x86: bound the composed nested TSC multiplier to the max ratio amirmohammad.eftekhar
2026-07-22  8:47 ` [PATCH] KVM: SVM: use wrmsrq_safe() when writing the nested TSC ratio MSR amirmohammad.eftekhar
2026-07-22  9:14   ` sashiko-bot
2026-07-22 13:51   ` Sean Christopherson
2026-07-22  9:16 ` [PATCH] KVM: x86: bound the composed nested TSC multiplier to the max ratio sashiko-bot

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.