From: Denis Rastyogin <ger...@altlinux.org> This error was discovered by fuzzing qemu-img.
Refcounts in an image may point beyond the end of the file. In this case, we cannot resize such images. Otherwise, we will at least catch it in qcow2_refcount_area() with assert(s->get_refcount(refblock_data, j) == 0). This check ensures that refcounts do not point beyond the end of the file. Signed-off-by: Denis Rastyogin <ger...@altlinux.org> --- block/qcow2.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/block/qcow2.c b/block/qcow2.c index 7774e7f090..b053a313f2 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4452,6 +4452,7 @@ qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, int64_t allocation_start, host_offset, guest_offset; int64_t clusters_allocated; int64_t old_file_size, last_cluster, new_file_size; + int64_t old_size, last_used_cluster; uint64_t nb_new_data_clusters, nb_new_l2_tables; bool subclusters_need_allocation = false; @@ -4480,6 +4481,27 @@ qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, old_file_size = ROUND_UP(old_file_size, s->cluster_size); } + /* Ensure that refcount does not point past the end of the actual file size. + * If refcount refers to regions beyond the file size, we can't properly call + * qcow2_refcount_area. */ + old_size = bdrv_co_getlength(bs); + if (old_size < 0) { + error_setg_errno(errp, -old_size, + "Failed to inquire current length"); + ret = old_size; + goto fail; + } + + last_used_cluster = qcow2_get_last_cluster(bs, old_size); + if (last_used_cluster >= old_file_size / s->cluster_size) { + error_setg(errp, + "Can't resize: last used cluster (%" PRId64 + ") exceeds file size (%" PRIu64 " clusters)", + last_used_cluster, old_file_size / s->cluster_size); + ret = -ERANGE; + goto fail; + } + nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) - start_of_cluster(s, old_length)) >> s->cluster_bits; -- 2.42.2