Merge tag 'kvmarm-fixes-6.3-4' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD

KVM/arm64 fixes for 6.3, part #4

- Plug a buffer overflow due to the use of the user-provided register
width for firmware regs. Outright reject accesses where the
user register width does not match the kernel representation.

- Protect non-atomic RMW operations on vCPU flags against preemption,
as an update to the flags by an intervening preemption could be lost.

+20 -1
+18 -1
arch/arm64/include/asm/kvm_host.h
··· 576 576 ({ \ 577 577 __build_check_flag(v, flagset, f, m); \ 578 578 \ 579 - v->arch.flagset & (m); \ 579 + READ_ONCE(v->arch.flagset) & (m); \ 580 580 }) 581 + 582 + /* 583 + * Note that the set/clear accessors must be preempt-safe in order to 584 + * avoid nesting them with load/put which also manipulate flags... 585 + */ 586 + #ifdef __KVM_NVHE_HYPERVISOR__ 587 + /* the nVHE hypervisor is always non-preemptible */ 588 + #define __vcpu_flags_preempt_disable() 589 + #define __vcpu_flags_preempt_enable() 590 + #else 591 + #define __vcpu_flags_preempt_disable() preempt_disable() 592 + #define __vcpu_flags_preempt_enable() preempt_enable() 593 + #endif 581 594 582 595 #define __vcpu_set_flag(v, flagset, f, m) \ 583 596 do { \ ··· 599 586 __build_check_flag(v, flagset, f, m); \ 600 587 \ 601 588 fset = &v->arch.flagset; \ 589 + __vcpu_flags_preempt_disable(); \ 602 590 if (HWEIGHT(m) > 1) \ 603 591 *fset &= ~(m); \ 604 592 *fset |= (f); \ 593 + __vcpu_flags_preempt_enable(); \ 605 594 } while (0) 606 595 607 596 #define __vcpu_clear_flag(v, flagset, f, m) \ ··· 613 598 __build_check_flag(v, flagset, f, m); \ 614 599 \ 615 600 fset = &v->arch.flagset; \ 601 + __vcpu_flags_preempt_disable(); \ 616 602 *fset &= ~(m); \ 603 + __vcpu_flags_preempt_enable(); \ 617 604 } while (0) 618 605 619 606 #define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__)
+2
arch/arm64/kvm/hypercalls.c
··· 397 397 u64 val; 398 398 int wa_level; 399 399 400 + if (KVM_REG_SIZE(reg->id) != sizeof(val)) 401 + return -ENOENT; 400 402 if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id))) 401 403 return -EFAULT; 402 404