diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 65eb26a05..ad5852f49 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2572,8 +2572,18 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, /* * Reserve memory ahead of time to avoid having to deal with failures * partway through setting the new attributes. + * + * The requested range can be arbitrarily large, so allow userspace to + * abort the reservation loop with a signal. Otherwise the loop can run + * for an extended period of time while holding slots_lock, which blocks + * tasks such as kvm_gmem_release() and triggers hung task warnings. */ for (i = start; i < end; i++) { + if (signal_pending(current)) { + r = -EINTR; + goto out_unlock; + } + r = xa_reserve(&kvm->mem_attr_array, i, GFP_KERNEL_ACCOUNT); if (r) goto out_unlock; @@ -2612,6 +2622,14 @@ static int kvm_vm_ioctl_set_mem_attributes(struct kvm *kvm, if (!PAGE_ALIGNED(attrs->address) || !PAGE_ALIGNED(attrs->size)) return -EINVAL; + /* + * KVM processes the range one gfn at a time while holding slots_lock. + * Reject absurdly large requests, using the same limit as memory + * regions, so that userspace can't hang tasks that need slots_lock. + */ + if ((attrs->size >> PAGE_SHIFT) > KVM_MEM_MAX_NR_PAGES) + return -EINVAL; + start = attrs->address >> PAGE_SHIFT; end = (attrs->address + attrs->size) >> PAGE_SHIFT;