diff options
author | Alberto Garcia <berto@igalia.com> | 2020-09-03 18:37:47 +0200 |
---|---|---|
committer | Max Reitz <mreitz@redhat.com> | 2020-09-15 11:05:13 +0200 |
commit | 184581fa4d8a83c601d967971f8460faeeb742ac (patch) | |
tree | 0f19b1668b549a99c653953faf0f1f9b4c811bed /block | |
parent | 02b1ecfa100e7ecc2306560cd27a4a2622bfeb04 (diff) | |
download | qemu-184581fa4d8a83c601d967971f8460faeeb742ac.zip qemu-184581fa4d8a83c601d967971f8460faeeb742ac.tar.gz qemu-184581fa4d8a83c601d967971f8460faeeb742ac.tar.bz2 |
qcow2: Fix removal of list members from BDRVQcow2State.cluster_allocs
When a write request needs to allocate new clusters (or change the L2
bitmap of existing ones) a QCowL2Meta structure is created so the L2
metadata can be later updated and any copy-on-write can be performed
if necessary.
A write request can span a region consisting of an arbitrary
combination of previously unallocated and allocated clusters, and if
the unallocated ones can be put contiguous to the existing ones then
QEMU will do so in order to minimize the number of write operations.
In practice this means that a write request has not just one but a
number of QCowL2Meta structures. All of them are added to the
cluster_allocs list that is stored in BDRVQcow2State and is used to
detect overlapping requests. After the write request finishes all its
associated QCowL2Meta are removed from that list. calculate_l2_meta()
takes care of creating and putting those structures in the list, and
qcow2_handle_l2meta() takes care of removing them.
The problem is that the error path in handle_alloc() also tries to
remove an item in that list, a remnant from the time when this was
handled there (that code would not even be correct anymore because
it only removes one struct and not all the ones from the same write
request).
This can trigger a double removal of the same item from the list,
causing a crash. This is not easy to reproduce in practice because
it requires that do_alloc_cluster_offset() fails after a successful
previous allocation during the same write request, but it can be
reproduced with the included test case.
Signed-off-by: Alberto Garcia <berto@igalia.com>
Message-Id: <3440a1c4d53c4fe48312b478c96accb338cbef7c.1599150873.git.berto@igalia.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/qcow2-cluster.c | 3 |
1 files changed, 0 insertions, 3 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 4530e5e..f65ccc5 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1710,9 +1710,6 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, out: qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - if (ret < 0 && *m && (*m)->nb_clusters > 0) { - QLIST_REMOVE(*m, next_in_flight); - } return ret; } |