* [Qemu-devel] [PATCH 0/2] block: Fix multiwrite error handling
@ 2010-07-01 14:31 Kevin Wolf
2010-07-01 14:31 ` [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite Kevin Wolf
2010-07-01 14:31 ` [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed Kevin Wolf
0 siblings, 2 replies; 7+ messages in thread
From: Kevin Wolf @ 2010-07-01 14:31 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf
The bdrv_aio_multiwrite error handling has some bugs that lead to premature
cleanup, causing use-after-free and double free problems.
Kevin Wolf (2):
block: Fix too early free in multiwrite
block: Handle multiwrite errors only when all requests have completed
block.c | 11 +++--------
1 files changed, 3 insertions(+), 8 deletions(-)
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite
2010-07-01 14:31 [Qemu-devel] [PATCH 0/2] block: Fix multiwrite error handling Kevin Wolf
@ 2010-07-01 14:31 ` Kevin Wolf
2010-07-02 8:10 ` Stefan Hajnoczi
2010-07-02 9:38 ` Christoph Hellwig
2010-07-01 14:31 ` [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed Kevin Wolf
1 sibling, 2 replies; 7+ messages in thread
From: Kevin Wolf @ 2010-07-01 14:31 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf
bdrv_aio_writev may call the callback immediately (and it will commonly do so
in error cases). If num_requests doesn't have its final value yet,
multiwrite_cb will falsely detect that all requests are completed and frees
the mcb. However, the mcb is still used by other requests that are started only
afterwards. When all requests are completed, it is freed for the second time.
Fix this by setting the right num_requests from the beginning.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block.c | 6 ++----
1 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/block.c b/block.c
index c40dd2c..9719649 100644
--- a/block.c
+++ b/block.c
@@ -2198,6 +2198,7 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
// Run the aio requests
+ mcb->num_requests = num_reqs;
for (i = 0; i < num_reqs; i++) {
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
reqs[i].nb_sectors, multiwrite_cb, mcb);
@@ -2206,16 +2207,13 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
// We can only fail the whole thing if no request has been
// submitted yet. Otherwise we'll wait for the submitted AIOs to
// complete and report the error in the callback.
- if (mcb->num_requests == 0) {
+ if (i == 0) {
reqs[i].error = -EIO;
goto fail;
} else {
- mcb->num_requests++;
multiwrite_cb(mcb, -EIO);
break;
}
- } else {
- mcb->num_requests++;
}
}
--
1.6.6.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed
2010-07-01 14:31 [Qemu-devel] [PATCH 0/2] block: Fix multiwrite error handling Kevin Wolf
2010-07-01 14:31 ` [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite Kevin Wolf
@ 2010-07-01 14:31 ` Kevin Wolf
2010-07-02 8:33 ` Stefan Hajnoczi
2010-07-02 9:40 ` Christoph Hellwig
1 sibling, 2 replies; 7+ messages in thread
From: Kevin Wolf @ 2010-07-01 14:31 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf
Don't try to be clever by freeing all temporary data and calling all callbacks
when the return value (an error) is certain. Doing so has at least two
important problems:
* The temporary data that is freed (qiov, possibly zero buffer) is still used
by the requests that have not yet completed.
* Calling the callbacks for all requests in the multiwrite means for the caller
that it may free buffers etc. which are still in use.
Just remember the error value and do the cleanup when all requests have
completed.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block.c | 5 +----
1 files changed, 1 insertions(+), 4 deletions(-)
diff --git a/block.c b/block.c
index 9719649..bff7d5a 100644
--- a/block.c
+++ b/block.c
@@ -2056,14 +2056,11 @@ static void multiwrite_cb(void *opaque, int ret)
if (ret < 0 && !mcb->error) {
mcb->error = ret;
- multiwrite_user_cb(mcb);
}
mcb->num_requests--;
if (mcb->num_requests == 0) {
- if (mcb->error == 0) {
- multiwrite_user_cb(mcb);
- }
+ multiwrite_user_cb(mcb);
qemu_free(mcb);
}
}
--
1.6.6.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite
2010-07-01 14:31 ` [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite Kevin Wolf
@ 2010-07-02 8:10 ` Stefan Hajnoczi
2010-07-02 9:38 ` Christoph Hellwig
1 sibling, 0 replies; 7+ messages in thread
From: Stefan Hajnoczi @ 2010-07-02 8:10 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel
On Thu, Jul 1, 2010 at 3:31 PM, Kevin Wolf <kwolf@redhat.com> wrote:
> bdrv_aio_writev may call the callback immediately (and it will commonly do so
> in error cases). If num_requests doesn't have its final value yet,
> multiwrite_cb will falsely detect that all requests are completed and frees
> the mcb. However, the mcb is still used by other requests that are started only
> afterwards. When all requests are completed, it is freed for the second time.
>
> Fix this by setting the right num_requests from the beginning.
Looks good to me.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
> block.c | 6 ++----
> 1 files changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/block.c b/block.c
> index c40dd2c..9719649 100644
> --- a/block.c
> +++ b/block.c
> @@ -2198,6 +2198,7 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
> num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
>
> // Run the aio requests
> + mcb->num_requests = num_reqs;
> for (i = 0; i < num_reqs; i++) {
> acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
> reqs[i].nb_sectors, multiwrite_cb, mcb);
> @@ -2206,16 +2207,13 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
> // We can only fail the whole thing if no request has been
> // submitted yet. Otherwise we'll wait for the submitted AIOs to
> // complete and report the error in the callback.
> - if (mcb->num_requests == 0) {
> + if (i == 0) {
> reqs[i].error = -EIO;
> goto fail;
> } else {
> - mcb->num_requests++;
> multiwrite_cb(mcb, -EIO);
> break;
> }
> - } else {
> - mcb->num_requests++;
> }
> }
>
> --
> 1.6.6.1
>
>
>
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed
2010-07-01 14:31 ` [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed Kevin Wolf
@ 2010-07-02 8:33 ` Stefan Hajnoczi
2010-07-02 9:40 ` Christoph Hellwig
1 sibling, 0 replies; 7+ messages in thread
From: Stefan Hajnoczi @ 2010-07-02 8:33 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel
On Thu, Jul 1, 2010 at 3:31 PM, Kevin Wolf <kwolf@redhat.com> wrote:
> Don't try to be clever by freeing all temporary data and calling all callbacks
> when the return value (an error) is certain. Doing so has at least two
> important problems:
>
> * The temporary data that is freed (qiov, possibly zero buffer) is still used
> by the requests that have not yet completed.
> * Calling the callbacks for all requests in the multiwrite means for the caller
> that it may free buffers etc. which are still in use.
>
> Just remember the error value and do the cleanup when all requests have
> completed.
>
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
> block.c | 5 +----
> 1 files changed, 1 insertions(+), 4 deletions(-)
>
> diff --git a/block.c b/block.c
> index 9719649..bff7d5a 100644
> --- a/block.c
> +++ b/block.c
> @@ -2056,14 +2056,11 @@ static void multiwrite_cb(void *opaque, int ret)
>
> if (ret < 0 && !mcb->error) {
> mcb->error = ret;
> - multiwrite_user_cb(mcb);
> }
>
> mcb->num_requests--;
> if (mcb->num_requests == 0) {
> - if (mcb->error == 0) {
> - multiwrite_user_cb(mcb);
> - }
> + multiwrite_user_cb(mcb);
I am a little confused at this point. Here is the meat of
bdrv_aio_multiwrite() from the kevin/devel branch:
// Run the aio requests
mcb->num_requests = num_reqs;
for (i = 0; i < num_reqs; i++) {
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
reqs[i].nb_sectors, multiwrite_cb, mcb);
if (acb == NULL) {
// We can only fail the whole thing if no request has been
// submitted yet. Otherwise we'll wait for the submitted AIOs to
// complete and report the error in the callback.
if (i == 0) {
reqs[i].error = -EIO;
goto fail;
} else {
multiwrite_cb(mcb, -EIO);
break;
}
}
}
If bdrv_aio_write() fails on request 2 out of 3, then
mcb->num_requests = 3 but only 2 requests were issued before breaking
the loop.
Now the 2 issued requests complete and call multiwrite_cb(). Since
was mcb->num_requests = 3, it never reaches zero and neither
multiwrite_user_cb(mcb) nor qemu_free(mcb) are ever called.
Is it safe to adjust mcb->num_requests = i before calling
multiwrite_cb() and breaking the loop in the failure case? That way
the num->num_requests will reach zero.
I hesitate a little because previous requests could have completed,
multiwrite_cb() was called, and mcb->num_requests was decremented?
Then the value of i cannot be used for mcb->num_requests because
previous requests it counts have already completed.
Stefan
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite
2010-07-01 14:31 ` [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite Kevin Wolf
2010-07-02 8:10 ` Stefan Hajnoczi
@ 2010-07-02 9:38 ` Christoph Hellwig
1 sibling, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2010-07-02 9:38 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel
On Thu, Jul 01, 2010 at 04:31:57PM +0200, Kevin Wolf wrote:
> bdrv_aio_writev may call the callback immediately (and it will commonly do so
> in error cases). If num_requests doesn't have its final value yet,
> multiwrite_cb will falsely detect that all requests are completed and frees
> the mcb. However, the mcb is still used by other requests that are started only
> afterwards. When all requests are completed, it is freed for the second time.
>
> Fix this by setting the right num_requests from the beginning.
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed
2010-07-01 14:31 ` [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed Kevin Wolf
2010-07-02 8:33 ` Stefan Hajnoczi
@ 2010-07-02 9:40 ` Christoph Hellwig
1 sibling, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2010-07-02 9:40 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel
On Thu, Jul 01, 2010 at 04:31:58PM +0200, Kevin Wolf wrote:
> Don't try to be clever by freeing all temporary data and calling all callbacks
> when the return value (an error) is certain. Doing so has at least two
> important problems:
>
> * The temporary data that is freed (qiov, possibly zero buffer) is still used
> by the requests that have not yet completed.
> * Calling the callbacks for all requests in the multiwrite means for the caller
> that it may free buffers etc. which are still in use.
>
> Just remember the error value and do the cleanup when all requests have
> completed.
Looks good.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-07-02 9:40 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-01 14:31 [Qemu-devel] [PATCH 0/2] block: Fix multiwrite error handling Kevin Wolf
2010-07-01 14:31 ` [Qemu-devel] [PATCH 1/2] block: Fix too early free in multiwrite Kevin Wolf
2010-07-02 8:10 ` Stefan Hajnoczi
2010-07-02 9:38 ` Christoph Hellwig
2010-07-01 14:31 ` [Qemu-devel] [PATCH 2/2] block: Handle multiwrite errors only when all requests have completed Kevin Wolf
2010-07-02 8:33 ` Stefan Hajnoczi
2010-07-02 9:40 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).