svn commit: r299185 - head/sys/dev/xen/privcmd
Roger Pau Monné
royger at FreeBSD.org
Fri May 6 16:44:47 UTC 2016
Author: royger
Date: Fri May 6 16:44:46 2016
New Revision: 299185
URL: https://svnweb.freebsd.org/changeset/base/299185
Log:
xen/privcmd: fix integer truncation in IOCTL_PRIVCMD_MMAPBATCH
The size field in the XENMEM_add_to_physmap_range is an uint16_t, and the
privcmd driver was doing an implicit truncation of an int into an uint16_t
when filling the hypercall parameters.
Fix this by adding a loop and making sure privcmd splits ioctl request into
2^16 chunks when issuing the hypercalls.
Reported and tested by: Marcin Cieslak <saper at saper.info>
Sponsored by: Citrix Systems R&D
Modified:
head/sys/dev/xen/privcmd/privcmd.c
Modified: head/sys/dev/xen/privcmd/privcmd.c
==============================================================================
--- head/sys/dev/xen/privcmd/privcmd.c Fri May 6 16:41:23 2016 (r299184)
+++ head/sys/dev/xen/privcmd/privcmd.c Fri May 6 16:44:46 2016 (r299185)
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/tree.h>
#include <sys/module.h>
#include <sys/proc.h>
+#include <sys/bitset.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
@@ -72,7 +73,7 @@ struct privcmd_map {
int pseudo_phys_res_id;
vm_paddr_t phys_base_addr;
boolean_t mapped;
- int *errs;
+ BITSET_DEFINE_VAR() *err;
};
static d_ioctl_t privcmd_ioctl;
@@ -138,7 +139,7 @@ retry:
rm.gpfn = atop(map->phys_base_addr) + i;
HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
}
- free(map->errs, M_PRIVCMD);
+ free(map->err, M_PRIVCMD);
}
error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
@@ -160,7 +161,7 @@ privcmd_pg_fault(vm_object_t object, vm_
return (VM_PAGER_FAIL);
pidx = OFF_TO_IDX(offset);
- if (pidx >= map->size || map->errs[pidx] != 0)
+ if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err))
return (VM_PAGER_FAIL);
page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
@@ -249,14 +250,15 @@ privcmd_ioctl(struct cdev *dev, unsigned
vm_map_t map;
vm_map_entry_t entry;
vm_object_t mem;
- vm_pindex_t index;
+ vm_pindex_t pindex;
vm_prot_t prot;
boolean_t wired;
struct xen_add_to_physmap_range add;
xen_ulong_t *idxs;
xen_pfn_t *gpfns;
- int *errs;
+ int *errs, index;
struct privcmd_map *umap;
+ uint16_t num;
mmap = (struct ioctl_privcmd_mmapbatch *)arg;
@@ -268,7 +270,7 @@ privcmd_ioctl(struct cdev *dev, unsigned
map = &td->td_proc->p_vmspace->vm_map;
error = vm_map_lookup(&map, mmap->addr, VM_PROT_NONE, &entry,
- &mem, &index, &prot, &wired);
+ &mem, &pindex, &prot, &wired);
if (error != KERN_SUCCESS) {
error = EINVAL;
break;
@@ -289,54 +291,72 @@ privcmd_ioctl(struct cdev *dev, unsigned
add.domid = DOMID_SELF;
add.space = XENMAPSPACE_gmfn_foreign;
- add.size = mmap->num;
add.foreign_domid = mmap->dom;
- idxs = malloc(sizeof(*idxs) * mmap->num, M_PRIVCMD,
- M_WAITOK | M_ZERO);
- gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD,
- M_WAITOK | M_ZERO);
- errs = malloc(sizeof(*errs) * mmap->num, M_PRIVCMD,
- M_WAITOK | M_ZERO);
+ /*
+ * The 'size' field in the xen_add_to_physmap_range only
+ * allows for UINT16_MAX mappings in a single hypercall.
+ */
+ num = MIN(mmap->num, UINT16_MAX);
+
+ idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK);
+ gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK);
+ errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK);
set_xen_guest_handle(add.idxs, idxs);
set_xen_guest_handle(add.gpfns, gpfns);
set_xen_guest_handle(add.errs, errs);
- error = copyin(&mmap->arr[0], idxs,
- sizeof(idxs[0]) * mmap->num);
- if (error != 0)
- goto mmap_out;
-
- for (i = 0; i < mmap->num; i++)
- gpfns[i] = atop(umap->phys_base_addr + i * PAGE_SIZE);
-
- error = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &add);
- if (error) {
- error = xen_translate_error(error);
- goto mmap_out;
- }
+ /* Allocate a bitset to store broken page mappings. */
+ umap->err = BITSET_ALLOC(mmap->num, M_PRIVCMD,
+ M_WAITOK | M_ZERO);
- for (i = 0; i < mmap->num; i++) {
- if (errs[i] != 0)
- errs[i] = xen_translate_error(errs[i]);
+ for (index = 0; index < mmap->num; index += num) {
+ num = MIN(mmap->num - index, UINT16_MAX);
+ add.size = num;
+
+ error = copyin(&mmap->arr[index], idxs,
+ sizeof(idxs[0]) * num);
+ if (error != 0)
+ goto mmap_out;
+
+ for (i = 0; i < num; i++)
+ gpfns[i] = atop(umap->phys_base_addr +
+ (i + index) * PAGE_SIZE);
+
+ bzero(errs, sizeof(*errs) * num);
+
+ error = HYPERVISOR_memory_op(
+ XENMEM_add_to_physmap_range, &add);
+ if (error != 0) {
+ error = xen_translate_error(error);
+ goto mmap_out;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (errs[i] != 0) {
+ errs[i] = xen_translate_error(errs[i]);
+
+ /* Mark the page as invalid. */
+ BIT_SET(mmap->num, index + i,
+ umap->err);
+ }
+ }
+
+ error = copyout(errs, &mmap->err[index],
+ sizeof(errs[0]) * num);
+ if (error != 0)
+ goto mmap_out;
}
- /*
- * Save errs, so we know which pages have been
- * successfully mapped.
- */
- umap->errs = errs;
umap->mapped = true;
- error = copyout(errs, &mmap->err[0],
- sizeof(errs[0]) * mmap->num);
-
mmap_out:
free(idxs, M_PRIVCMD);
free(gpfns, M_PRIVCMD);
+ free(errs, M_PRIVCMD);
if (!umap->mapped)
- free(errs, M_PRIVCMD);
+ free(umap->err, M_PRIVCMD);
break;
}
More information about the svn-src-head
mailing list