diff options
author | Kevin Wolf <kwolf@redhat.com> | 2021-07-08 13:47:04 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2021-07-09 13:19:11 +0200 |
commit | bcfd86d6a6432be75fd8700c7c1aabb243adf469 (patch) | |
tree | c8c2aafd77c22ea17f8632a46715fcbec3517cd2 /block | |
parent | a7cd44bef3d9380181734a93977c3d1df3eef2cf (diff) | |
download | qemu-bcfd86d6a6432be75fd8700c7c1aabb243adf469.zip qemu-bcfd86d6a6432be75fd8700c7c1aabb243adf469.tar.gz qemu-bcfd86d6a6432be75fd8700c7c1aabb243adf469.tar.bz2 |
qcow2: Fix dangling pointer after reopen for 'file'
Without an external data file, s->data_file is a second pointer with the
same value as bs->file. When changing bs->file to a different BdrvChild
and freeing the old BdrvChild, s->data_file must also be updated,
otherwise it points to freed memory and causes crashes.
This problem was caught by iotests case 245.
Fixes: df2b7086f169239ebad5d150efa29c9bb6d4f820
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20210708114709.206487-2-kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/qcow2.c | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/block/qcow2.c b/block/qcow2.c index 0cac2ed..9f1b646 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1926,6 +1926,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + BDRVQcow2State *s = state->bs->opaque; Qcow2ReopenState *r; int ret; @@ -1956,6 +1957,16 @@ static int qcow2_reopen_prepare(BDRVReopenState *state, } } + /* + * Without an external data file, s->data_file points to the same BdrvChild + * as bs->file. It needs to be resynced after reopen because bs->file may + * be changed. We can't use it in the meantime. + */ + if (!has_data_file(state->bs)) { + assert(s->data_file == state->bs->file); + s->data_file = NULL; + } + return 0; fail: @@ -1966,7 +1977,16 @@ fail: static void qcow2_reopen_commit(BDRVReopenState *state) { + BDRVQcow2State *s = state->bs->opaque; + qcow2_update_options_commit(state->bs, state->opaque); + if (!s->data_file) { + /* + * If we don't have an external data file, s->data_file was cleared by + * qcow2_reopen_prepare() and needs to be updated. + */ + s->data_file = state->bs->file; + } g_free(state->opaque); } @@ -1990,6 +2010,15 @@ static void qcow2_reopen_commit_post(BDRVReopenState *state) static void qcow2_reopen_abort(BDRVReopenState *state) { + BDRVQcow2State *s = state->bs->opaque; + + if (!s->data_file) { + /* + * If we don't have an external data file, s->data_file was cleared by + * qcow2_reopen_prepare() and needs to be restored. + */ + s->data_file = state->bs->file; + } qcow2_update_options_abort(state->bs, state->opaque); g_free(state->opaque); } |