• Sean Christopherson's avatar
    KVM: x86/mmu: Grab memslot for correct address space in NX recovery worker · 817fa998
    Sean Christopherson authored
    Factor in the address space (non-SMM vs. SMM) of the target shadow page
    when recovering potential NX huge pages, otherwise KVM will retrieve the
    wrong memslot when zapping shadow pages that were created for SMM.  The
    bug most visibly manifests as a WARN on the memslot being non-NULL, but
    the worst case scenario is that KVM could unaccount the shadow page
    without ensuring KVM won't install a huge page, i.e. if the non-SMM slot
    is being dirty logged, but the SMM slot is not.
    
     ------------[ cut here ]------------
     WARNING: CPU: 1 PID: 3911 at arch/x86/kvm/mmu/mmu.c:7015
     kvm_nx_huge_page_recovery_worker+0x38c/0x3d0 [kvm]
     CPU: 1 PID: 3911 Comm: kvm-nx-lpage-re
     RIP: 0010:kvm_nx_huge_page_recovery_worker+0x38c/0x3d0 [kvm]
     RSP: 0018:ffff99b284f0be68 EFLAGS: 00010246
     RAX: 0000000000000000 RBX: ffff99b284edd000 RCX: 0000000000000000
     RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
     RBP: ffff9271397024e0 R08: 0000000000000000 R09: ffff927139702450
     R10: 0000000000000000 R11: 0000000000000001 R12: ffff99b284f0be98
     R13: 0000000000000000 R14: ffff9270991fcd80 R15: 0000000000000003
     FS:  0000000000000000(0000) GS:ffff927f9f640000(0000) knlGS:0000000000000000
     CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
     CR2: 00007f0aacad3ae0 CR3: 000000088fc2c005 CR4: 00000000003726e0
     Call Trace:
      <TASK>
    __pfx_kvm_nx_huge_page_recovery_worker+0x10/0x10 [kvm]
      kvm_vm_worker_thread+0x106/0x1c0 [kvm]
      kthread+0xd9/0x100
      ret_from_fork+0x2c/0x50
      </TASK>
     ---[ end trace 0000000000000000 ]---
    
    This bug was exposed by commit edbdb43f ("KVM: x86: Preserve TDP MMU
    roots until they are explicitly invalidated"), which allowed KVM to retain
    SMM TDP MMU roots effectively indefinitely.  Before commit edbdb43f,
    KVM would zap all SMM TDP MMU roots and thus all SMM TDP MMU shadow pages
    once all vCPUs exited SMM, which made the window where this bug (recovering
    an SMM NX huge page) could be encountered quite tiny.  To hit the bug, the
    NX recovery thread would have to run while at least one vCPU was in SMM.
    Most VMs typically only use SMM during boot, and so the problematic shadow
    pages were gone by the time the NX recovery thread ran.
    
    Now that KVM preserves TDP MMU roots until they are explicitly invalidated
    (e.g. by a memslot deletion), the window to trigger the bug is effectively
    never closed because most VMMs don't delete memslots after boot (except
    for a handful of special scenarios).
    
    Fixes: eb298605 ("KVM: x86/mmu: Do not recover dirty-tracked NX Huge Pages")
    Reported-by: default avatarFabio Coatti <fabio.coatti@gmail.com>
    Closes: https://lore.kernel.org/all/CADpTngX9LESCdHVu_2mQkNGena_Ng2CphWNwsRGSMxzDsTjU2A@mail.gmail.com
    Cc: stable@vger.kernel.org
    Link: https://lore.kernel.org/r/20230602010137.784664-1-seanjc@google.comSigned-off-by: default avatarSean Christopherson <seanjc@google.com>
    817fa998
mmu.c 198 KB