qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 10/17] block-backend: Fix potential double bl


From: Paolo Bonzini
Subject: Re: [Qemu-devel] [PATCH v2 10/17] block-backend: Fix potential double blk_delete()
Date: Thu, 13 Sep 2018 17:19:52 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1

On 13/09/2018 14:52, Kevin Wolf wrote:
> blk_unref() first decreases the refcount of the BlockBackend and calls
> blk_delete() if the refcount reaches zero. Requests can still be in
> flight at this point, they are only drained during blk_delete():
> 
> At this point, arbitrary callbacks can run. If any callback takes a
> temporary BlockBackend reference, it will first increase the refcount to
> 1 and then decrease it to 0 again, triggering another blk_delete(). This
> will cause a use-after-free crash in the outer blk_delete().
> 
> Fix it by draining the BlockBackend before decreasing to refcount to 0.
> Assert in blk_ref() that it never takes the first refcount (which would
> mean that the BlockBackend is already being deleted).
> 
> Signed-off-by: Kevin Wolf <address@hidden>
> Reviewed-by: Fam Zheng <address@hidden>
> ---
>  block/block-backend.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/block/block-backend.c b/block/block-backend.c
> index efa8d8011c..1b2d7a6ff5 100644
> --- a/block/block-backend.c
> +++ b/block/block-backend.c
> @@ -435,6 +435,7 @@ int blk_get_refcnt(BlockBackend *blk)
>   */
>  void blk_ref(BlockBackend *blk)
>  {
> +    assert(blk->refcnt > 0);
>      blk->refcnt++;
>  }
>  
> @@ -447,7 +448,11 @@ void blk_unref(BlockBackend *blk)
>  {
>      if (blk) {
>          assert(blk->refcnt > 0);
> -        if (!--blk->refcnt) {
> +        if (blk->refcnt > 1) {
> +            blk->refcnt--;
> +        } else {
> +            blk_drain(blk);

Maybe "assert(blk->refcnt == 1);" here to ensure that blk_drain() cannot
resurrect the block device, or alternatively

+    if (blk->refcnt == 1) {
+        blk_drain(blk);
+        assert(blk->refcnt == 1);
+    }
     if (!--blk->refcnt) {
         blk_delete(blk);
     }

Paolo



reply via email to

[Prev in Thread] Current Thread [Next in Thread]