diff options
Diffstat (limited to 'block/qcow2-refcount.c')
| -rw-r--r-- | block/qcow2-refcount.c | 40 |
1 files changed, 39 insertions, 1 deletions
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index aa3fd6cf17..3de1ab51ba 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -367,6 +367,13 @@ static int alloc_refcount_block(BlockDriverState *bs, return new_block; } + /* If we're allocating the block at offset 0 then something is wrong */ + if (new_block == 0) { + qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " + "allocation of refcount block at offset 0"); + return -EIO; + } + #ifdef DEBUG_ALLOC2 fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 " at %" PRIx64 "\n", @@ -1075,6 +1082,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) return new_cluster; } + if (new_cluster == 0) { + qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " + "allocation of compressed cluster " + "at offset 0"); + return -EIO; + } + if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { offset = new_cluster; free_in_cluster = s->cluster_size; @@ -3063,16 +3077,40 @@ done: return ret; } +static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset) +{ + BDRVQcow2State *s = bs->opaque; + uint32_t index = offset_to_reftable_index(s, offset); + int64_t covering_refblock_offset = 0; + + if (index < s->refcount_table_size) { + covering_refblock_offset = s->refcount_table[index] & REFT_OFFSET_MASK; + } + if (!covering_refblock_offset) { + qcow2_signal_corruption(bs, true, -1, -1, "Refblock at %#" PRIx64 " is " + "not covered by the refcount structures", + offset); + return -EIO; + } + + return covering_refblock_offset; +} + static int qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) { BDRVQcow2State *s = bs->opaque; - uint64_t refblock_offs = get_refblock_offset(s, discard_block_offs); + int64_t refblock_offs; uint64_t cluster_index = discard_block_offs >> s->cluster_bits; uint32_t block_index = cluster_index & (s->refcount_block_size - 1); void *refblock; int ret; + refblock_offs = get_refblock_offset(bs, discard_block_offs); + if (refblock_offs < 0) { + return refblock_offs; + } + assert(discard_block_offs != 0); ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, |