KVM: x86: Fix broken debugregs ABI for 32 bit kernels
authorMathias Krause <minipli@grsecurity.net>
Sat, 3 Feb 2024 12:45:22 +0000 (13:45 +0100)
committerSean Christopherson <seanjc@google.com>
Mon, 5 Feb 2024 23:40:54 +0000 (15:40 -0800)
The ioctl()s to get and set KVM's debug registers are broken for 32 bit
kernels as they'd only copy half of the user register state because of a
UAPI and in-kernel type mismatch (__u64 vs. unsigned long; 8 vs. 4
bytes).

This makes it impossible for userland to set anything but DR0 without
resorting to bit folding tricks.

Switch to a loop for copying debug registers that'll implicitly do the
type conversion for us, if needed.

There are likely no users (left) for 32bit KVM, fix the bug nonetheless.

Fixes: a1efbe77c1fd ("KVM: x86: Add support for saving&restoring debug registers")
Signed-off-by: Mathias Krause <minipli@grsecurity.net>
Link: https://lore.kernel.org/r/20240203124522.592778-4-minipli@grsecurity.net
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/x86.c

index 709844fcd52117b30993461a9e14fa0b9a9e3272..7eb7736cfd767f17ab13744d586b5fa0c1a5e78e 100644 (file)
@@ -5510,9 +5510,14 @@ static void kvm_vcpu_ioctl_x86_get_debugregs(struct kvm_vcpu *vcpu,
                                             struct kvm_debugregs *dbgregs)
 {
        unsigned long val;
+       unsigned int i;
 
        memset(dbgregs, 0, sizeof(*dbgregs));
-       memcpy(dbgregs->db, vcpu->arch.db, sizeof(vcpu->arch.db));
+
+       BUILD_BUG_ON(ARRAY_SIZE(vcpu->arch.db) != ARRAY_SIZE(dbgregs->db));
+       for (i = 0; i < ARRAY_SIZE(vcpu->arch.db); i++)
+               dbgregs->db[i] = vcpu->arch.db[i];
+
        kvm_get_dr(vcpu, 6, &val);
        dbgregs->dr6 = val;
        dbgregs->dr7 = vcpu->arch.dr7;
@@ -5521,6 +5526,8 @@ static void kvm_vcpu_ioctl_x86_get_debugregs(struct kvm_vcpu *vcpu,
 static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
                                            struct kvm_debugregs *dbgregs)
 {
+       unsigned int i;
+
        if (dbgregs->flags)
                return -EINVAL;
 
@@ -5529,7 +5536,9 @@ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
        if (!kvm_dr7_valid(dbgregs->dr7))
                return -EINVAL;
 
-       memcpy(vcpu->arch.db, dbgregs->db, sizeof(vcpu->arch.db));
+       for (i = 0; i < ARRAY_SIZE(vcpu->arch.db); i++)
+               vcpu->arch.db[i] = dbgregs->db[i];
+
        kvm_update_dr0123(vcpu);
        vcpu->arch.dr6 = dbgregs->dr6;
        vcpu->arch.dr7 = dbgregs->dr7;