diff options
author | Kevin Wolf <kwolf@redhat.com> | 2014-05-20 17:12:47 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2014-08-15 15:07:15 +0200 |
commit | de82815db1c89da058b7fb941dab137d6d9ab738 (patch) | |
tree | 782b541f1a0c5d154d2c8a166306e7407094bf9a /block/qcow2-cluster.c | |
parent | 0df93305f21712e975ab5df260cc5a91e5daafca (diff) |
qcow2: Handle failure for potentially large allocations
Some code in the block layer makes potentially huge allocations. Failure
is not completely unexpected there, so avoid aborting qemu and handle
out-of-memory situations gracefully.
This patch addresses the allocations in the qcow2 block driver.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'block/qcow2-cluster.c')
-rw-r--r-- | block/qcow2-cluster.c | 36 |
1 files changed, 28 insertions, 8 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 4208dc08b5..e7c5f486cd 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -72,14 +72,20 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, #endif new_l1_size2 = sizeof(uint64_t) * new_l1_size; - new_l1_table = g_malloc0(align_offset(new_l1_size2, 512)); + new_l1_table = qemu_try_blockalign(bs->file, + align_offset(new_l1_size2, 512)); + if (new_l1_table == NULL) { + return -ENOMEM; + } + memset(new_l1_table, 0, align_offset(new_l1_size2, 512)); + memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); /* write new table (align to cluster) */ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); if (new_l1_table_offset < 0) { - g_free(new_l1_table); + qemu_vfree(new_l1_table); return new_l1_table_offset; } @@ -113,7 +119,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, if (ret < 0) { goto fail; } - g_free(s->l1_table); + qemu_vfree(s->l1_table); old_l1_table_offset = s->l1_table_offset; s->l1_table_offset = new_l1_table_offset; s->l1_table = new_l1_table; @@ -123,7 +129,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, QCOW2_DISCARD_OTHER); return 0; fail: - g_free(new_l1_table); + qemu_vfree(new_l1_table); qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2, QCOW2_DISCARD_OTHER); return ret; @@ -372,7 +378,10 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, } iov.iov_len = n * BDRV_SECTOR_SIZE; - iov.iov_base = qemu_blockalign(bs, iov.iov_len); + iov.iov_base = qemu_try_blockalign(bs, iov.iov_len); + if (iov.iov_base == NULL) { + return -ENOMEM; + } qemu_iovec_init_external(&qiov, &iov, 1); @@ -702,7 +711,11 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); assert(m->nb_clusters > 0); - old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t)); + old_cluster = g_try_malloc(m->nb_clusters * sizeof(uint64_t)); + if (old_cluster == NULL) { + ret = -ENOMEM; + goto err; + } /* copy content of unmodified sectors */ ret = perform_cow(bs, m, &m->cow_start); @@ -1562,7 +1575,10 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, if (!is_active_l1) { /* inactive L2 tables require a buffer to be stored in when loading * them from disk */ - l2_table = qemu_blockalign(bs, s->cluster_size); + l2_table = qemu_try_blockalign(bs->file, s->cluster_size); + if (l2_table == NULL) { + return -ENOMEM; + } } for (i = 0; i < l1_size; i++) { @@ -1740,7 +1756,11 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs) nb_clusters = size_to_clusters(s, bs->file->total_sectors * BDRV_SECTOR_SIZE); - expanded_clusters = g_malloc0((nb_clusters + 7) / 8); + expanded_clusters = g_try_malloc0((nb_clusters + 7) / 8); + if (expanded_clusters == NULL) { + ret = -ENOMEM; + goto fail; + } ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, &expanded_clusters, &nb_clusters); |