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
next prev 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®