Unmappable memory cannot be shared with the host. Ensure that the private attribute cannot be removed from unmappable memory. Signed-off-by: Fuad Tabba <tabba@xxxxxxxxxx> --- virt/kvm/kvm_main.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9f6ff314bda3..adfee6592f6c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -52,6 +52,7 @@ #include <linux/lockdep.h> #include <linux/kthread.h> #include <linux/suspend.h> +#include <linux/rcupdate_wait.h> #include <asm/processor.h> #include <asm/ioctl.h> @@ -2464,6 +2465,40 @@ bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, return has_attrs; } +/* + * Returns true if _any_ gfn in the range [@start, @end) has _any_ attribute + * matching @attr. + */ +static bool kvm_any_range_has_memory_attribute(struct kvm *kvm, gfn_t start, + gfn_t end, unsigned long attr) +{ + XA_STATE(xas, &kvm->mem_attr_array, start); + bool has_attr = false; + void *entry; + + rcu_read_lock(); + xas_for_each(&xas, entry, end - 1) { + if (xas_retry(&xas, entry)) + continue; + + if (!xa_is_value(entry)) + continue; + + if ((xa_to_value(entry) & attr) == attr) { + has_attr = true; + break; + } + + if (need_resched()) { + xas_pause(&xas); + cond_resched_rcu(); + } + } + rcu_read_unlock(); + + return has_attr; +} + static u64 kvm_supported_mem_attributes(struct kvm *kvm) { if (!kvm || kvm_arch_has_private_mem(kvm)) @@ -2614,6 +2649,14 @@ static int __kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, r = -EPERM; goto out_unlock; } + + /* Unmappable memory cannot be shared. */ + if (!(attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE) && + kvm_any_range_has_memory_attribute(kvm, start, end, + KVM_MEMORY_ATTRIBUTE_NOT_MAPPABLE)) { + r = -EPERM; + goto out_unlock; + } } /* -- 2.44.0.rc1.240.g4c46232300-goog