* [PATCH 0/2] KVM: x86: Fix mostly theoretical undefined behavior
@ 2021-09-29 22:24 Sean Christopherson
2021-09-29 22:24 ` [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks Sean Christopherson
2021-09-29 22:24 ` [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT Sean Christopherson
0 siblings, 2 replies; 8+ messages in thread
From: Sean Christopherson @ 2021-09-29 22:24 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson,
Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d,
Alexander Potapenko
Fix a mostly theoretical undefined behavior bug due to consuming
uninitialized data when searching for a matching CPUID entry during vCPU
RESET/INIT. The bug is mostly theoretical because it requires very
aggressive inlining from the compiler, as well as deliberate "sabotage"
from the compiler (which _is_ allowed by the C standard) in the face of
known uninitialized data.
Patch 1, the "fix" that is tagged for stable, is all kinds of gross in that
it doesn't directly address uninitialized data, and instead tweaks a low
level CPUID helper to avoid consuming the uninitialized data. I went that
route for the fix so that the fix would be easily/directy consumable
downstream, as porting the fix from v5.15-rcN to literally any other buggy
kernel would require hand coding the fix due to refactoring and code
movement across files.
Patch 2 directly addresses the uninitialized data.
If patch 1 is unpalatable, an alternative would be to do a bit of merge
magic and feed in a fix to initialize "dummy" in svm.c, which was the only
buggy path prior to v5.15-rcN. However, KVM lived from 2012-2020 with
what's effectively the behavior after applying patch 1, and no one noticed
that the behavior was broken in 2020 until v5.15-rc1 introduced the bad
behavior to VMX, i.e. opened up the validation surface to bots that
presumably run the majority of their cycles on Intel CPUs.
Sean Christopherson (2):
KVM: x86: Swap order of CPUID entry "index" vs. "significant flag"
checks
KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT
arch/x86/kvm/cpuid.c | 4 ++--
arch/x86/kvm/x86.c | 11 +++++------
2 files changed, 7 insertions(+), 8 deletions(-)
--
2.33.0.685.g46640cef36-goog
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks 2021-09-29 22:24 [PATCH 0/2] KVM: x86: Fix mostly theoretical undefined behavior Sean Christopherson @ 2021-09-29 22:24 ` Sean Christopherson 2021-09-29 22:51 ` Jim Mattson 2021-09-29 22:24 ` [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT Sean Christopherson 1 sibling, 1 reply; 8+ messages in thread From: Sean Christopherson @ 2021-09-29 22:24 UTC (permalink / raw) To: Paolo Bonzini Cc: Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko Check whether a CPUID entry's index is significant before checking for a matching index to hack-a-fix an undefined behavior bug due to consuming uninitialized data. RESET/INIT emulation uses kvm_cpuid() to retrieve CPUID.0x1, which does _not_ have a significant index, and fails to initialize the dummy variable that doubles as EBX/ECX/EDX output _and_ ECX, a.k.a. index, input. Practically speaking, it's _extremely_ unlikely any compiler will yield code that causes problems, as the compiler would need to inline the kvm_cpuid() call to detect the uninitialized data, and intentionally hose the kernel, e.g. insert ud2, instead of simply ignoring the result of the index comparison. Although the sketchy "dummy" pattern was introduced in SVM by commit 66f7b72e1171 ("KVM: x86: Make register state after reset conform to specification"), it wasn't actually broken until commit 7ff6c0350315 ("KVM: x86: Remove stateful CPUID handling") arbitrarily swapped the order of operations such that "index" was checked before the significant flag. Avoid consuming uninitialized data by reverting to checking the flag before the index purely so that the fix can be easily backported; the offending RESET/INIT code has been refactored, moved, and consolidated from vendor code to common x86 since the bug was introduced. A future patch will directly address the bad RESET/INIT behavior. The undefined behavior was detected by syzbot + KernelMemorySanitizer. BUG: KMSAN: uninit-value in cpuid_entry2_find arch/x86/kvm/cpuid.c:68 BUG: KMSAN: uninit-value in kvm_find_cpuid_entry arch/x86/kvm/cpuid.c:1103 BUG: KMSAN: uninit-value in kvm_cpuid+0x456/0x28f0 arch/x86/kvm/cpuid.c:1183 cpuid_entry2_find arch/x86/kvm/cpuid.c:68 [inline] kvm_find_cpuid_entry arch/x86/kvm/cpuid.c:1103 [inline] kvm_cpuid+0x456/0x28f0 arch/x86/kvm/cpuid.c:1183 kvm_vcpu_reset+0x13fb/0x1c20 arch/x86/kvm/x86.c:10885 kvm_apic_accept_events+0x58f/0x8c0 arch/x86/kvm/lapic.c:2923 vcpu_enter_guest+0xfd2/0x6d80 arch/x86/kvm/x86.c:9534 vcpu_run+0x7f5/0x18d0 arch/x86/kvm/x86.c:9788 kvm_arch_vcpu_ioctl_run+0x245b/0x2d10 arch/x86/kvm/x86.c:10020 Local variable ----dummy@kvm_vcpu_reset created at: kvm_vcpu_reset+0x1fb/0x1c20 arch/x86/kvm/x86.c:10812 kvm_apic_accept_events+0x58f/0x8c0 arch/x86/kvm/lapic.c:2923 Reported-by: syzbot+f3985126b746b3d59c9d@syzkaller.appspotmail.com Reported-by: Alexander Potapenko <glider@google.com> Fixes: 2a24be79b6b7 ("KVM: VMX: Set EDX at INIT with CPUID.0x1, Family-Model-Stepping") Fixes: 7ff6c0350315 ("KVM: x86: Remove stateful CPUID handling") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson <seanjc@google.com> --- arch/x86/kvm/cpuid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index a02a8b0408ff..2d70edb0f323 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -72,8 +72,8 @@ static inline struct kvm_cpuid_entry2 *cpuid_entry2_find( for (i = 0; i < nent; i++) { e = &entries[i]; - if (e->function == function && (e->index == index || - !(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX))) + if (e->function == function && + (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) || e->index == index)) return e; } -- 2.33.0.685.g46640cef36-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks 2021-09-29 22:24 ` [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks Sean Christopherson @ 2021-09-29 22:51 ` Jim Mattson 0 siblings, 0 replies; 8+ messages in thread From: Jim Mattson @ 2021-09-29 22:51 UTC (permalink / raw) To: Sean Christopherson Cc: Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko On Wed, Sep 29, 2021 at 3:24 PM Sean Christopherson <seanjc@google.com> wrote: > > Check whether a CPUID entry's index is significant before checking for a > matching index to hack-a-fix an undefined behavior bug due to consuming > uninitialized data. RESET/INIT emulation uses kvm_cpuid() to retrieve > CPUID.0x1, which does _not_ have a significant index, and fails to > initialize the dummy variable that doubles as EBX/ECX/EDX output _and_ > ECX, a.k.a. index, input. > > Practically speaking, it's _extremely_ unlikely any compiler will yield > code that causes problems, as the compiler would need to inline the > kvm_cpuid() call to detect the uninitialized data, and intentionally hose > the kernel, e.g. insert ud2, instead of simply ignoring the result of > the index comparison. > > Although the sketchy "dummy" pattern was introduced in SVM by commit > 66f7b72e1171 ("KVM: x86: Make register state after reset conform to > specification"), it wasn't actually broken until commit 7ff6c0350315 > ("KVM: x86: Remove stateful CPUID handling") arbitrarily swapped the > order of operations such that "index" was checked before the significant > flag. > > Avoid consuming uninitialized data by reverting to checking the flag > before the index purely so that the fix can be easily backported; the > offending RESET/INIT code has been refactored, moved, and consolidated > from vendor code to common x86 since the bug was introduced. A future > patch will directly address the bad RESET/INIT behavior. > > The undefined behavior was detected by syzbot + KernelMemorySanitizer. > > BUG: KMSAN: uninit-value in cpuid_entry2_find arch/x86/kvm/cpuid.c:68 > BUG: KMSAN: uninit-value in kvm_find_cpuid_entry arch/x86/kvm/cpuid.c:1103 > BUG: KMSAN: uninit-value in kvm_cpuid+0x456/0x28f0 arch/x86/kvm/cpuid.c:1183 > cpuid_entry2_find arch/x86/kvm/cpuid.c:68 [inline] > kvm_find_cpuid_entry arch/x86/kvm/cpuid.c:1103 [inline] > kvm_cpuid+0x456/0x28f0 arch/x86/kvm/cpuid.c:1183 > kvm_vcpu_reset+0x13fb/0x1c20 arch/x86/kvm/x86.c:10885 > kvm_apic_accept_events+0x58f/0x8c0 arch/x86/kvm/lapic.c:2923 > vcpu_enter_guest+0xfd2/0x6d80 arch/x86/kvm/x86.c:9534 > vcpu_run+0x7f5/0x18d0 arch/x86/kvm/x86.c:9788 > kvm_arch_vcpu_ioctl_run+0x245b/0x2d10 arch/x86/kvm/x86.c:10020 > > Local variable ----dummy@kvm_vcpu_reset created at: > kvm_vcpu_reset+0x1fb/0x1c20 arch/x86/kvm/x86.c:10812 > kvm_apic_accept_events+0x58f/0x8c0 arch/x86/kvm/lapic.c:2923 > > Reported-by: syzbot+f3985126b746b3d59c9d@syzkaller.appspotmail.com > Reported-by: Alexander Potapenko <glider@google.com> > Fixes: 2a24be79b6b7 ("KVM: VMX: Set EDX at INIT with CPUID.0x1, Family-Model-Stepping") > Fixes: 7ff6c0350315 ("KVM: x86: Remove stateful CPUID handling") > Cc: stable@vger.kernel.org > Signed-off-by: Sean Christopherson <seanjc@google.com> Reviewed-by: Jim Mattson <jmattson@google.com> ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT 2021-09-29 22:24 [PATCH 0/2] KVM: x86: Fix mostly theoretical undefined behavior Sean Christopherson 2021-09-29 22:24 ` [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks Sean Christopherson @ 2021-09-29 22:24 ` Sean Christopherson 2021-09-29 22:49 ` Jim Mattson 2021-09-30 8:25 ` Paolo Bonzini 1 sibling, 2 replies; 8+ messages in thread From: Sean Christopherson @ 2021-09-29 22:24 UTC (permalink / raw) To: Paolo Bonzini Cc: Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko Manually look for a CPUID.0x1 entry instead of bouncing through kvm_cpuid() when retrieving the Family-Model-Stepping information for vCPU RESET/INIT. This fixes a potential undefined behavior bug due to kvm_cpuid() using the uninitialized "dummy" param as the ECX _input_, a.k.a. the index. A more minimal fix would be to simply zero "dummy", but the extra work in kvm_cpuid() is wasteful, and KVM should be treating the FMS retrieval as an out-of-band access, e.g. same as how KVM computes guest.MAXPHYADDR. Both Intel's SDM and AMD's APM describe the RDX value at RESET/INIT as holding the CPU's FMS information, not as holding CPUID.0x1.EAX. KVM's usage of CPUID entries to get FMS is simply a pragmatic approach to avoid having yet another way for userspace to provide inconsistent data. No functional change intended. Signed-off-by: Sean Christopherson <seanjc@google.com> --- arch/x86/kvm/x86.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 46ee9bf61df4..14562ea5d78d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10941,9 +10941,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) { + struct kvm_cpuid_entry2 *cpuid_0x1; unsigned long old_cr0 = kvm_read_cr0(vcpu); unsigned long new_cr0; - u32 eax, dummy; /* * Several of the "set" flows, e.g. ->set_cr0(), read other registers @@ -11025,12 +11025,11 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) * if no CPUID match is found. Note, it's impossible to get a match at * RESET since KVM emulates RESET before exposing the vCPU to userspace, * i.e. it'simpossible for kvm_cpuid() to find a valid entry on RESET. - * But, go through the motions in case that's ever remedied. + * But, go through the motions in case that's ever remedied. Note, the + * index for CPUID.0x1 is not significant, arbitrarily specify '0'. */ - eax = 1; - if (!kvm_cpuid(vcpu, &eax, &dummy, &dummy, &dummy, true)) - eax = 0x600; - kvm_rdx_write(vcpu, eax); + cpuid_0x1 = kvm_find_cpuid_entry(vcpu, 1, 0); + kvm_rdx_write(vcpu, cpuid_0x1 ? cpuid_0x1->eax : 0x600); vcpu->arch.ia32_xss = 0; -- 2.33.0.685.g46640cef36-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT 2021-09-29 22:24 ` [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT Sean Christopherson @ 2021-09-29 22:49 ` Jim Mattson 2021-09-30 8:25 ` Paolo Bonzini 1 sibling, 0 replies; 8+ messages in thread From: Jim Mattson @ 2021-09-29 22:49 UTC (permalink / raw) To: Sean Christopherson Cc: Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko On Wed, Sep 29, 2021 at 3:24 PM Sean Christopherson <seanjc@google.com> wrote: > > Manually look for a CPUID.0x1 entry instead of bouncing through > kvm_cpuid() when retrieving the Family-Model-Stepping information for > vCPU RESET/INIT. This fixes a potential undefined behavior bug due to > kvm_cpuid() using the uninitialized "dummy" param as the ECX _input_, > a.k.a. the index. > > A more minimal fix would be to simply zero "dummy", but the extra work in > kvm_cpuid() is wasteful, and KVM should be treating the FMS retrieval as > an out-of-band access, e.g. same as how KVM computes guest.MAXPHYADDR. > Both Intel's SDM and AMD's APM describe the RDX value at RESET/INIT as > holding the CPU's FMS information, not as holding CPUID.0x1.EAX. KVM's > usage of CPUID entries to get FMS is simply a pragmatic approach to avoid > having yet another way for userspace to provide inconsistent data. > > No functional change intended. > > Signed-off-by: Sean Christopherson <seanjc@google.com> Reviewed-by: Jim Mattson <jmattson@google.com> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT 2021-09-29 22:24 ` [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT Sean Christopherson 2021-09-29 22:49 ` Jim Mattson @ 2021-09-30 8:25 ` Paolo Bonzini 2021-09-30 15:11 ` Sean Christopherson 1 sibling, 1 reply; 8+ messages in thread From: Paolo Bonzini @ 2021-09-30 8:25 UTC (permalink / raw) To: Sean Christopherson Cc: Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko On 30/09/21 00:24, Sean Christopherson wrote: > * RESET since KVM emulates RESET before exposing the vCPU to userspace, > * i.e. it'simpossible for kvm_cpuid() to find a valid entry on RESET. > + * But, go through the motions in case that's ever remedied. Note, the > + * index for CPUID.0x1 is not significant, arbitrarily specify '0'. Just one nit, this comment change is not really needed because almost all callers are using '0' for the same reason. But, perhaps adding kvm_find_cpuid_entry_index and removing the last parameter from kvm_find_cpuid_entry would be a good idea. Also, the kvm_cpuid() reference needs to be changed, which I did upon commit. Paolo > */ > - eax = 1; > - if (!kvm_cpuid(vcpu, &eax, &dummy, &dummy, &dummy, true)) > - eax = 0x600; > - kvm_rdx_write(vcpu, eax); > + cpuid_0x1 = kvm_find_cpuid_entry(vcpu, 1, 0); > + kvm_rdx_write(vcpu, cpuid_0x1 ? cpuid_0x1->eax : 0x600); ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT 2021-09-30 8:25 ` Paolo Bonzini @ 2021-09-30 15:11 ` Sean Christopherson 2021-09-30 16:45 ` Paolo Bonzini 0 siblings, 1 reply; 8+ messages in thread From: Sean Christopherson @ 2021-09-30 15:11 UTC (permalink / raw) To: Paolo Bonzini Cc: Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko On Thu, Sep 30, 2021, Paolo Bonzini wrote: > On 30/09/21 00:24, Sean Christopherson wrote: > > * RESET since KVM emulates RESET before exposing the vCPU to userspace, > > * i.e. it'simpossible for kvm_cpuid() to find a valid entry on RESET. > > + * But, go through the motions in case that's ever remedied. Note, the > > + * index for CPUID.0x1 is not significant, arbitrarily specify '0'. > > Just one nit, this comment change is not really needed because almost all > callers are using '0' for the same reason. > > But, perhaps adding kvm_find_cpuid_entry_index and removing the last > parameter from kvm_find_cpuid_entry would be a good idea. I like this idea, but only if callers are forced to specify the index when the index is significant, e.g. add a magic CPUID_INDEX_DONT_CARE and WARN in cpuid_entry2_find() if index is significant and index == DONT_CARE. I'll fiddle with this, unless you want the honors? > Also, the kvm_cpuid() reference needs to be changed, which I did upon > commit. Doh, thanks! ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT 2021-09-30 15:11 ` Sean Christopherson @ 2021-09-30 16:45 ` Paolo Bonzini 0 siblings, 0 replies; 8+ messages in thread From: Paolo Bonzini @ 2021-09-30 16:45 UTC (permalink / raw) To: Sean Christopherson Cc: Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel, kvm, linux-kernel, syzbot+f3985126b746b3d59c9d, Alexander Potapenko On 30/09/21 17:11, Sean Christopherson wrote: >> >> But, perhaps adding kvm_find_cpuid_entry_index and removing the last >> parameter from kvm_find_cpuid_entry would be a good idea. > I like this idea, but only if callers are forced to specify the index when the > index is significant, e.g. add a magic CPUID_INDEX_DONT_CARE and WARN in > cpuid_entry2_find() if index is significant and index == DONT_CARE. Yeah, or it can just be that kvm_find_cpuid_entry passes -1 which acts as the magic value. > I'll fiddle with this, unless you want the honors? Not really. :) Thanks, Paolo ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2021-09-30 16:45 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2021-09-29 22:24 [PATCH 0/2] KVM: x86: Fix mostly theoretical undefined behavior Sean Christopherson 2021-09-29 22:24 ` [PATCH 1/2] KVM: x86: Swap order of CPUID entry "index" vs. "significant flag" checks Sean Christopherson 2021-09-29 22:51 ` Jim Mattson 2021-09-29 22:24 ` [PATCH 2/2] KVM: x86: Manually retrieve CPUID.0x1 when getting FMS for RESET/INIT Sean Christopherson 2021-09-29 22:49 ` Jim Mattson 2021-09-30 8:25 ` Paolo Bonzini 2021-09-30 15:11 ` Sean Christopherson 2021-09-30 16:45 ` Paolo Bonzini
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox