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