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 | __build_check_flag(v, flagset, f, m); \ | 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 | #define __vcpu_set_flag(v, flagset, f, m) \ | 595 | #define __vcpu_set_flag(v, flagset, f, m) \ |
| 583 | do { \ | 596 | do { \ |
| 584 | typeof(v->arch.flagset) *fset; \ | 597 | typeof(v->arch.flagset) *fset; \ |
| @@ -586,9 +599,11 @@ struct kvm_vcpu_arch { · 1 unmodified line | |||
| 586 | __build_check_flag(v, flagset, f, m); \ | 599 | __build_check_flag(v, flagset, f, m); \ |
| 587 | \ | 600 | \ |
| 588 | fset = &v->arch.flagset; \ | 601 | fset = &v->arch.flagset; \ |
| 602 | __vcpu_flags_preempt_disable(); \ | ||
| 589 | if (HWEIGHT(m) > 1) \ | 603 | if (HWEIGHT(m) > 1) \ |
| 590 | *fset &= ~(m); \ | 604 | *fset &= ~(m); \ |
| 591 | *fset |= (f); \ | 605 | *fset |= (f); \ |
| 606 | __vcpu_flags_preempt_enable(); \ | ||
| 592 | } while (0) | 607 | } while (0) |
| 593 | 608 | ||
| 594 | #define __vcpu_clear_flag(v, flagset, f, m) \ | 609 | #define __vcpu_clear_flag(v, flagset, f, m) \ |
| @@ -598,7 +613,9 @@ struct kvm_vcpu_arch { · 3 unmodified lines | |||
| 598 | __build_check_flag(v, flagset, f, m); \ | 613 | __build_check_flag(v, flagset, f, m); \ |
| 599 | \ | 614 | \ |
| 600 | fset = &v->arch.flagset; \ | 615 | fset = &v->arch.flagset; \ |
| 616 | __vcpu_flags_preempt_disable(); \ | ||
| 601 | *fset &= ~(m); \ | 617 | *fset &= ~(m); \ |
| 618 | __vcpu_flags_preempt_enable(); \ | ||
| 602 | } while (0) | 619 | } while (0) |
| 603 | 620 | ||
| 604 | #define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__) | 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 | u64 val; | 397 | u64 val; |
| 398 | int wa_level; | 398 | int wa_level; |
| 399 | 399 | ||
| 400 | if (KVM_REG_SIZE(reg->id) != sizeof(val)) | ||
| 401 | return -ENOENT; | ||
| 400 | if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id))) | 402 | if (copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id))) |
| 401 | return -EFAULT; | 403 | return -EFAULT; |
| 402 | 404 | ||