mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Sean Christopherson <seanjc@google.com>,
	Paolo Bonzini <pbonzini@redhat.com>
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Amirmohammad Eftekhar <amirmohammad.eftekhar@cispa.de>,
	Sashiko Bot <sashiko-bot@kernel.org>
Subject: [PATCH v3 03/21] KVM: x86: Allow userspace to set KVM's max supported guest TSC frequency
Date: Wed, 30 Sep 2026 10:36:17 -0700	[thread overview]
Message-ID: <20260930173635.3362655-4-seanjc@google.com> (raw)
In-Reply-To: <20260930173635.3362655-1-seanjc@google.com>

Reject KVM_SET_TSC_KHZ if the incoming frequency is strictly greater than
KVM's max supported frequency, not if the frequency is greater than *or*
equal to the max frequency.  The effective off-by-one bug came about via
(dubious) review feedback, which also subtly collided with a functional
change in later versions of the original TSC scaling series.

In v2 of the original TSC scaling series[1], KVM set its absolute min/max
to [1, UINT_MAX], i.e. allowed any value that would fit in the u32 passed
to KVM_SET_TSC_KHZ[2].

  min = max(1ULL,          __scale_tsc(tsc_khz, TSC_RATIO_MIN));
  max = min(0xffffffffULL, __scale_tsc(tsc_khz, TSC_RATIO_MAX));

That prompted Avi to suggest rejecting the "equals" case, presumably
because the check would always succeed given an absolute max of UINT_MAX?
But even that doesn't hold up to scrutiny, as allowing the actual min/max
isn't inherently unsafe.  Regardless, that feedback was taken and applied
to future versions, but only for the maximum, not the minimum.

  > +
  > +		r = -EINVAL;
  > +		if (user_tsc_khz<  kvm_min_guest_tsc_khz ||
  > +		    user_tsc_khz>  kvm_max_guest_tsc_khz)

  <= and >= are probably safer.

v3 of the series[3] then also realized that allowing UINT_MAX would lead to
undesirable interactions with KVM_GET_TSC_KHZ, which returns a *signed*
32-bit integer that is implicitly converted into a signed 64-bit value on
64-bit kernels.  I.e. allowing a value greater than INT_MAX would result
in userspace observing a negative value when doing KVM_GET_TSC_KHZ after
KVM_SET_TSC_KHZ.

 /*
  * Make sure the user can only configure tsc_khz values that
  * fit into a signed integer.
  * A min value is not calculated needed because it will always
  * be 1 on all machines and a value of 0 is used to disable
  * tsc-scaling for the vcpu.
  */
  max = min(0x7fffffffULL, __scale_tsc(tsc_khz, TSC_RATIO_MAX));

  kvm_max_guest_tsc_khz = max;

v3 also dropped the explicit minimum tracking, as both AMD and Intel
support a minimum *fractional* ratio of 1, i.e. AMD and Intel support a
minimum frequency of "host / 2^32" and "host / 2^48" respectively.  And
because KVM_GET_TSC_KHZ (and KVM itself) only supports frequencies that fit
in a signed 32-bit integer, even AMD's more coarse-grained ratio can scale
down any host frequency to '1', i.e. KVM can always support a minimum
frequency of 1KHz.  Rather than explicitly reject a frequency of 1KHz,
v3 simply dropped the minimum check, i.e. ignored the "<=" suggestion, but
kept the ">=" side of things.

As a result, KVM now has a bizarre uABI where KVM_SET_TSC_KHZ tops out at
INT_MAX-1 for no discernible reason.  Fix the off-by-one flaw to provide a
less weird uABI, and so that KVM_SET_TSC_KHZ accepts the maximum possible
value that can be returned by KVM_GET_TSC_KHZ (without running afoul of
casting issues; KVM doesn't actually sanity check that tsc_khz fits in a
signed 32-bit value, which is a non-issue in practice because the units are
KHz, not Hz, i.e. KVM_GET_TSC_KHZ is fine until CPUs with a TSC frequency
greater than ~2.147PHz come along).

Note, in practice, no real world VMM is likely to care.  As above, running
afoul of the off-by-one issue would mean trying to configure a virtual TSC
frequency that is three orders of magnitude greater than what current CPUs
support.

Opportunistically explain *why* KVM restricts KVM_SET_TSC_KHZ to values
that fit in a signed integer, as it requires far too much spelunking to
piece together the connection to KVM_GET_TSC_KHZ.

Link: https://lore.kernel.org/all/1300952424-32014-1-git-send-email-joerg.roedel@amd.com [1]
Link: https://lore.kernel.org/all/1300952424-32014-7-git-send-email-joerg.roedel@amd.com [2]
Link: https://lore.kernel.org/all/1301042691-22929-7-git-send-email-joerg.roedel@amd.com [3]
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/x86.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 037c7ea5c5a7..1d25ffcf9273 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3718,7 +3718,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		user_tsc_khz = (u32)arg;
 
 		if (kvm_caps.has_tsc_control &&
-		    user_tsc_khz >= kvm_caps.max_guest_tsc_khz)
+		    user_tsc_khz > kvm_caps.max_guest_tsc_khz)
 			goto out;
 
 		if (user_tsc_khz == 0)
@@ -4685,7 +4685,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
 		user_tsc_khz = (u32)arg;
 
 		if (kvm_caps.has_tsc_control &&
-		    user_tsc_khz >= kvm_caps.max_guest_tsc_khz)
+		    user_tsc_khz > kvm_caps.max_guest_tsc_khz)
 			goto out;
 
 		if (user_tsc_khz == 0)
@@ -7174,7 +7174,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
 	if (kvm_caps.has_tsc_control) {
 		/*
 		 * Make sure the user can only configure tsc_khz values that
-		 * fit into a signed integer.
+		 * fit into a signed integer, otherwise KVM_GET_TSC_KHZ would
+		 * return a negative value and confuse userspace.
 		 * A min value is not calculated because it will always
 		 * be 1 on all machines.
 		 */
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


  parent reply	other threads:[~2026-09-30 17:37 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-30 17:36 [PATCH v3 00/21] KVM: x86: Fix nested TSC scaling edge cases Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 01/21] KVM: x86: Saturate L2's TSC frequency if it exceeds hardware supports Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 02/21] KVM: SVM: Fallback to the default TSC ratio if KVM tries to use a bad multiplier Sean Christopherson
2026-09-30 17:36 ` Sean Christopherson [this message]
2026-09-30 17:36 ` [PATCH v3 04/21] KVM: selftests: Use KVM's pRNG to randomize L1's TSC ratio in TSC scaling test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 05/21] KVM: selftests: Drop redundant VMWRITE of TSC_MULTIPLIER_HIGH Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 06/21] KVM: selftests: Drop unnecessary use of PRIu64 in nested TSC scaling test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 07/21] KVM: selftests: Randomize L2's scale factor " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 08/21] KVM: selftests: Rename TSC freq checkers " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 09/21] KVM: selftests: Extract guts of nested TSC scaling test to helper function Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 10/21] KVM: selftests: Track L2 multiplier, not scale-up factor, in nested TSC test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 11/21] KVM: selftests: Print out the failing L{0,1,2} level in nested TSC scaling test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 12/21] KVM: selftests: Allow +/- 1 tolerance if expected TSC frequency is <100 Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 13/21] KVM: selftests: Use KVM's reported TSC KHz as L0's frequency (sanity checked) Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 14/21] KVM: selftests: Explicitly pass TSC frequencies to guts of TSC scaling test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 15/21] KVM: selftests: Sanity check KVM's default TSC freq in nested " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 16/21] KVM: selftests: Test L1 "up" and L2 "down" " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 17/21] KVM: selftests: Verify that KVM saturates L2 TSC freq on {under,over}flow Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 18/21] KVM: selftests: Test non-zero TSC offset on SVM in nested TSC scaling test Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 19/21] KVM: selftests: Randomize L2's TSC offset in the " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 20/21] KVM: selftests: Use GUEST_SYNC2() in " Sean Christopherson
2026-09-30 17:36 ` [PATCH v3 21/21] KVM: selftests: Spell out UCALL in nested TSC scaling test's enums Sean Christopherson

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=20260930173635.3362655-4-seanjc@google.com \
    --to=seanjc@google.com \
    --cc=amirmohammad.eftekhar@cispa.de \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=sashiko-bot@kernel.org \
    /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®