qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH v3] Do not delete BlockDriverState when deleting


From: Markus Armbruster
Subject: Re: [Qemu-devel] [PATCH v3] Do not delete BlockDriverState when deleting the drive
Date: Tue, 29 Mar 2011 09:40:58 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux)

Ryan Harper <address@hidden> writes:

> When removing a drive from the host-side via drive_del we currently have the
> following path:
>
> drive_del
> qemu_aio_flush()
> bdrv_close()    // zaps bs->drv, which makes any subsequent I/O get
>                 // dropped.  Works as designed
> drive_uninit()
> bdrv_delete()   // frees the bs.  Since the device is still connected to
>                 // bs, any subsequent I/O is a use-after-free.
>
> The value of bs->drv becomes unpredictable on free.  As long as it
> remains null, I/O still gets dropped, however it could become non-null at any
> point after the free resulting SEGVs or other QEMU state corruption.
>
> To resolve this issue as simply as possible, we can chose to not actually
> delete the BlockDriverState pointer.  Since bdrv_close() handles setting the 
> drv
> pointer to NULL, we just need to remove the BlockDriverState from the QLIST
> that is used to enumerate the block devices.  This is currently handled within
> bdrv_delete, so move this into its own function, bdrv_make_anon().
>
> The result is that we can now invoke drive_del, this closes the file 
> descriptors
> and sets BlockDriverState->drv to NULL which prevents futher IO to the device,
> and since we do not free BlockDriverState, we don't have to worry about the 
> copy
> retained in the block devices.
>
> We also don't attempt to remove the qdev property since we are no longer 
> deleting
> the BlockDriverState on drives with associated drives.  This also allows for
> removing Drives with no devices associated either.
>
> Reported-by: Markus Armbruster <address@hidden>
> Signed-off-by: Ryan Harper <address@hidden>
> ---
> v2->v3
>  - Update drive_del use after free description
>  - s/bdrv_remove/bdrv_make_anon/g
>  - Don't remove qdev property since we don't delete bs any more
>  - If (bs->peer) bdrv_make_anon else bdrv_delete to handle removing
>    drives with no device.
> v1->v2
>   - NULL bs->device_name after removing from list to prevent
>     second removal.
>
>  block.c    |   13 ++++++++++---
>  block.h    |    1 +
>  blockdev.c |   25 ++++++++-----------------
>  3 files changed, 19 insertions(+), 20 deletions(-)
>
> diff --git a/block.c b/block.c
> index c8e2f97..6a5d3f2 100644
> --- a/block.c
> +++ b/block.c
> @@ -697,14 +697,21 @@ void bdrv_close_all(void)
>      }
>  }
>  
> +/* make a BlockDriverState anonymous by removing from bdrv_state list.
> +   Also, NULL terminate the device_name to prevent double remove */
> +void bdrv_make_anon(BlockDriverState *bs)
> +{
> +    if (bs->device_name[0] != '\0') {
> +        QTAILQ_REMOVE(&bdrv_states, bs, list);
> +    }

You lost

+    bs->device_name[0] = '\0';

since v2.  Oops.

> +}
> +
>  void bdrv_delete(BlockDriverState *bs)
>  {
>      assert(!bs->peer);
>  
>      /* remove from list, if necessary */
> -    if (bs->device_name[0] != '\0') {
> -        QTAILQ_REMOVE(&bdrv_states, bs, list);
> -    }
> +    bdrv_make_anon(bs);
>  
>      bdrv_close(bs);
>      if (bs->file != NULL) {
> diff --git a/block.h b/block.h
> index 5d78fc0..52e9cad 100644
> --- a/block.h
> +++ b/block.h
> @@ -66,6 +66,7 @@ int bdrv_create(BlockDriver *drv, const char* filename,
>      QEMUOptionParameter *options);
>  int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
>  BlockDriverState *bdrv_new(const char *device_name);
> +void bdrv_make_anon(BlockDriverState *bs);
>  void bdrv_delete(BlockDriverState *bs);
>  int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
>  int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
> diff --git a/blockdev.c b/blockdev.c
> index ecf2252..2c0eb06 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -737,8 +737,6 @@ int do_drive_del(Monitor *mon, const QDict *qdict, 
> QObject **ret_data)
>  {
>      const char *id = qdict_get_str(qdict, "id");
>      BlockDriverState *bs;
> -    BlockDriverState **ptr;
> -    Property *prop;
>  
>      bs = bdrv_find(id);
>      if (!bs) {
> @@ -755,24 +753,17 @@ int do_drive_del(Monitor *mon, const QDict *qdict, 
> QObject **ret_data)
>      bdrv_flush(bs);
>      bdrv_close(bs);
>  
> -    /* clean up guest state from pointing to host resource by
> -     * finding and removing DeviceState "drive" property */
> +    /* if we have a device associated with this BlockDriverState (bs->peer)
> +     * then we need to make the drive anonymous until the device
> +     * can be removed.  If this is a drive with no device backing
> +     * then we can just get rid of the block driver state right here.
> +     */
>      if (bs->peer) {
> -        for (prop = bs->peer->info->props; prop && prop->name; prop++) {
> -            if (prop->info->type == PROP_TYPE_DRIVE) {
> -                ptr = qdev_get_prop_ptr(bs->peer, prop);
> -                if (*ptr == bs) {
> -                    bdrv_detach(bs, bs->peer);
> -                    *ptr = NULL;
> -                    break;
> -                }
> -            }
> -        }
> +        bdrv_make_anon(bs);
> +    } else {
> +        bdrv_delete(bs);

Don't you need drive_uninit() here?

>      }
>  
> -    /* clean up host side */
> -    drive_uninit(drive_get_by_blockdev(bs));
> -
>      return 0;
>  }
>  
> -- 
> 1.7.1



reply via email to

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