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.
Unified · Split
| @@ -576,9 +576,22 @@ struct kvm_vcpu_arch { · 575 unmodified lines | ||
| 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 | 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 | |
| 594 | + | |
| 582 | 595 | #define __vcpu_set_flag(v, flagset, f, m) \ |
| 583 | 596 | do { \ |
| 584 | 597 | typeof(v->arch.flagset) *fset; \ |
| @@ -586,9 +599,11 @@ struct kvm_vcpu_arch { · 1 unmodified line | ||
| 586 | 599 | __build_check_flag(v, flagset, f, m); \ |
| 587 | 600 | \ |
| 588 | 601 | fset = &v->arch.flagset; \ |
| 602 | + __vcpu_flags_preempt_disable(); \ | |
| 589 | 603 | if (HWEIGHT(m) > 1) \ |
| 590 | 604 | *fset &= ~(m); \ |
| 591 | 605 | *fset |= (f); \ |
| 606 | + __vcpu_flags_preempt_enable(); \ | |
| 592 | 607 | } while (0) |
| 593 | 608 | |
| 594 | 609 | #define __vcpu_clear_flag(v, flagset, f, m) \ |
| @@ -598,7 +613,9 @@ struct kvm_vcpu_arch { · 3 unmodified lines | ||
| 598 | 613 | __build_check_flag(v, flagset, f, m); \ |
| 599 | 614 | \ |
| 600 | 615 | fset = &v->arch.flagset; \ |
| 616 | + __vcpu_flags_preempt_disable(); \ | |
| 601 | 617 | *fset &= ~(m); \ |
| 618 | + __vcpu_flags_preempt_enable(); \ | |
| 602 | 619 | } while (0) |
| 603 | 620 | |
| 604 | 621 | #define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__) |
| @@ -397,6 +397,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) · 396 unmodified lines | ||
| 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 | |