• Sean Christopherson's avatar
    KVM: x86/mmu: Fix per-cpu counter corruption on 32-bit builds · d5aaad6f
    Sean Christopherson authored
    Take a signed 'long' instead of an 'unsigned long' for the number of
    pages to add/subtract to the total number of pages used by the MMU.  This
    fixes a zero-extension bug on 32-bit kernels that effectively corrupts
    the per-cpu counter used by the shrinker.
    
    Per-cpu counters take a signed 64-bit value on both 32-bit and 64-bit
    kernels, whereas kvm_mod_used_mmu_pages() takes an unsigned long and thus
    an unsigned 32-bit value on 32-bit kernels.  As a result, the value used
    to adjust the per-cpu counter is zero-extended (unsigned -> signed), not
    sign-extended (signed -> signed), and so KVM's intended -1 gets morphed to
    4294967295 and effectively corrupts the counter.
    
    This was found by a staggering amount of sheer dumb luck when running
    kvm-unit-tests on a 32-bit KVM build.  The shrinker just happened to kick
    in while running tests and do_shrink_slab() logged an error about trying
    to free a negative number of objects.  The truly lucky part is that the
    kernel just happened to be a slightly stale build, as the shrinker no
    longer yells about negative objects as of commit 18bb473e ("mm:
    vmscan: shrink deferred objects proportional to priority").
    
     vmscan: shrink_slab: mmu_shrink_scan+0x0/0x210 [kvm] negative objects to delete nr=-858993460
    
    Fixes: bc8a3d89 ("kvm: mmu: Fix overflow on kvm mmu page limit calculation")
    Cc: stable@vger.kernel.org
    Cc: Ben Gardon <bgardon@google.com>
    Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
    Message-Id: <20210804214609.1096003-1-seanjc@google.com>
    Reviewed-by: default avatarJim Mattson <jmattson@google.com>
    Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
    d5aaad6f
mmu.c 162 KB