},
};
+static int vfio_mmap_compare_offset(const void *a, const void *b)
+{
+ const VFIOMmap *mmap_a = a;
+ const VFIOMmap *mmap_b = b;
+
+ if (mmap_a->offset < mmap_b->offset) {
+ return -1;
+ } else if (mmap_a->offset > mmap_b->offset) {
+ return 1;
+ }
+ return 0;
+}
+
static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
struct vfio_region_info *info)
{
region->nr_mmaps = j;
region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
+ /*
+ * Sort sparse mmaps by offset to ensure proper handling of gaps
+ * and predictable mapping order in vfio_region_mmap().
+ */
+ if (region->nr_mmaps > 1) {
+ qsort(region->mmaps, region->nr_mmaps, sizeof(VFIOMmap),
+ vfio_mmap_compare_offset);
+
+ /*
+ * Validate that sparse regions don't overlap after sorting.
+ */
+ for (i = 1; i < region->nr_mmaps; i++) {
+ off_t prev_end = region->mmaps[i - 1].offset +
+ region->mmaps[i - 1].size;
+ if (prev_end > region->mmaps[i].offset) {
+ error_report("%s: overlapping sparse mmap regions detected "
+ "in region %d: [0x%"PRIx64"-0x%"PRIx64"] overlaps "
+ "with [0x%"PRIx64"-0x%"PRIx64"]",
+ __func__, region->nr, region->mmaps[i - 1].offset,
+ prev_end - 1, region->mmaps[i].offset,
+ region->mmaps[i].offset + region->mmaps[i].size - 1);
+ g_free(region->mmaps);
+ region->mmaps = NULL;
+ region->nr_mmaps = 0;
+ return -EINVAL;
+ }
+ }
+ }
+
return 0;
}
ret = vfio_setup_region_sparse_mmaps(region, info);
- if (ret) {
+ if (ret == -ENODEV) {
region->nr_mmaps = 1;
region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
region->mmaps[0].offset = 0;
region->mmaps[0].size = region->size;
+ } else if (ret) {
+ return ret;
}
}
}