@samitouri / QOSamiQemu / commits / ac52279673

parallels: validate bitmap L1 table size before allocating it

parallels_load_bitmap() allocated the L1 table sized directly from the untrusted l1_size field, only cross-checking it against the bitmap's actual size after the allocation and the L1 table copy had already happened. Compute the expected size and reject a mismatch before touching the allocator, instead of after. Signed-off-by: Denis V. Lunev <den@openvz.org> CC: Thomas Huth <thuth@redhat.com> CC: Stefan Hajnoczi <stefanha@redhat.com>

Denis V. Lunev committed Jul 22, 2026 at 18:54 UTC ac52279673625b48e14243c4121bacf47efbed79
1 file changed +20 -13
block/parallels-ext.c
+20 -13
@@ -70,20 +70,11 @@ parallels_load_bitmap_data(BlockDriverState *bs, const uint64_t *l1_table,
70 uint64_t offset, limit;
71 uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
72 uint8_t *buf = NULL;
73 - uint64_t i, tab_size =
74 - DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
75 - s->cluster_size);
76 -
77 - if (tab_size != l1_size) {
78 - error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
79 - "to bitmap size and cluster size. Expected %" PRIu64,
80 - l1_size, tab_size);
81 - return -EINVAL;
82 - }
73 + uint64_t i;
74
75 buf = qemu_blockalign(bs, s->cluster_size);
76 limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
86 - for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
77 + for (i = 0, offset = 0; i < l1_size; ++i, offset += limit) {
78 uint64_t count = MIN(bm_size - offset, limit);
79 uint64_t entry = l1_table[i];
80
@@ -124,12 +115,14 @@ static BdrvDirtyBitmap * GRAPH_RDLOCK
115 parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size,
116 Error **errp)
117 {
118 + BDRVParallelsState *s = bs->opaque;
119 int ret;
120 ParallelsDirtyBitmapFeature bf;
121 g_autofree uint64_t *l1_table = NULL;
122 BdrvDirtyBitmap *bitmap;
123 QemuUUID uuid;
124 char uuidstr[UUID_STR_LEN];
125 + uint64_t bm_size, tab_size;
126 int i;
127
128 if (data_size < sizeof(bf)) {
@@ -164,6 +157,17 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size,
157 return NULL;
158 }
159
160 + bm_size = bdrv_dirty_bitmap_size(bitmap);
161 + tab_size = DIV_ROUND_UP(
162 + bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
163 + s->cluster_size);
164 + if (tab_size != bf.l1_size) {
165 + error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
166 + "to bitmap size and cluster size. Expected %" PRIu64,
167 + bf.l1_size, tab_size);
168 + goto fail;
169 + }
170 +
171 l1_table = g_new(uint64_t, bf.l1_size);
172 for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
173 l1_table[i] = ldq_le_p(data);
@@ -171,8 +175,7 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size,
175
176 ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
177 if (ret < 0) {
174 - bdrv_release_dirty_bitmap(bitmap);
175 - return NULL;
178 + goto fail;
179 }
180
181 /* We support format extension only for RO parallels images. */
@@ -180,6 +183,10 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size,
183 bdrv_dirty_bitmap_set_readonly(bitmap, true);
184
185 return bitmap;
186 +
187 +fail:
188 + bdrv_release_dirty_bitmap(bitmap);
189 + return NULL;
190 }
191
192 static int GRAPH_RDLOCK