in privcmd.c [615:701]
static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
{
struct privcmd_data *data = file->private_data;
struct privcmd_dm_op kdata;
struct privcmd_dm_op_buf *kbufs;
unsigned int nr_pages = 0;
struct page **pages = NULL;
struct xen_dm_op_buf *xbufs = NULL;
unsigned int i;
long rc;
unsigned int pinned = 0;
if (copy_from_user(&kdata, udata, sizeof(kdata)))
return -EFAULT;
/* If restriction is in place, check the domid matches */
if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
return -EPERM;
if (kdata.num == 0)
return 0;
if (kdata.num > privcmd_dm_op_max_num)
return -E2BIG;
kbufs = kcalloc(kdata.num, sizeof(*kbufs), GFP_KERNEL);
if (!kbufs)
return -ENOMEM;
if (copy_from_user(kbufs, kdata.ubufs,
sizeof(*kbufs) * kdata.num)) {
rc = -EFAULT;
goto out;
}
for (i = 0; i < kdata.num; i++) {
if (kbufs[i].size > privcmd_dm_op_buf_max_size) {
rc = -E2BIG;
goto out;
}
if (!access_ok(kbufs[i].uptr,
kbufs[i].size)) {
rc = -EFAULT;
goto out;
}
nr_pages += DIV_ROUND_UP(
offset_in_page(kbufs[i].uptr) + kbufs[i].size,
PAGE_SIZE);
}
pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL);
if (!pages) {
rc = -ENOMEM;
goto out;
}
xbufs = kcalloc(kdata.num, sizeof(*xbufs), GFP_KERNEL);
if (!xbufs) {
rc = -ENOMEM;
goto out;
}
rc = lock_pages(kbufs, kdata.num, pages, nr_pages, &pinned);
if (rc < 0) {
nr_pages = pinned;
goto out;
}
for (i = 0; i < kdata.num; i++) {
set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr);
xbufs[i].size = kbufs[i].size;
}
xen_preemptible_hcall_begin();
rc = HYPERVISOR_dm_op(kdata.dom, kdata.num, xbufs);
xen_preemptible_hcall_end();
out:
unlock_pages(pages, nr_pages);
kfree(xbufs);
kfree(pages);
kfree(kbufs);
return rc;
}