Skip to content

Commit

Permalink
blockjob: update nodes head while removing all bdrv
Browse files Browse the repository at this point in the history
block_job_remove_all_bdrv() iterates through job->nodes, calling
bdrv_root_unref_child() for each entry. The call to the latter may
reach child_job_[can_]set_aio_ctx(), which will also attempt to
traverse job->nodes, potentially finding entries that where freed
on previous iterations.

To avoid this situation, update job->nodes head on each iteration to
ensure that already freed entries are no longer linked to the list.

RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1746631
Signed-off-by: Sergio Lopez <[email protected]>
Cc: [email protected]
Signed-off-by: Max Reitz <[email protected]>
Message-id: [email protected]
Reviewed-by: Sergio Lopez <[email protected]>
Signed-off-by: Max Reitz <[email protected]>
  • Loading branch information
slp authored and XanClic committed Sep 16, 2019
1 parent c34dc07 commit d876bf6
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions blockjob.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,23 @@ static const BdrvChildRole child_job = {

void block_job_remove_all_bdrv(BlockJob *job)
{
GSList *l;
for (l = job->nodes; l; l = l->next) {
/*
* bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(),
* which will also traverse job->nodes, so consume the list one by
* one to make sure that such a concurrent access does not attempt
* to process an already freed BdrvChild.
*/
while (job->nodes) {
GSList *l = job->nodes;
BdrvChild *c = l->data;

job->nodes = l->next;

bdrv_op_unblock_all(c->bs, job->blocker);
bdrv_root_unref_child(c);

g_slist_free_1(l);
}
g_slist_free(job->nodes);
job->nodes = NULL;
}

bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs)
Expand Down

0 comments on commit d876bf6

Please sign in to comment.