diff mbox series

[1/5] block/qcow2-bitmap: Skip length check in some cases

Message ID 20190305234337.18353-2-jsnow@redhat.com
State New
Headers show
Series block/qcow2-bitmap: Enable resize with persistent bitmaps | expand

Commit Message

John Snow March 5, 2019, 11:43 p.m. UTC
If we were to allow resizes, the length check that happens when we load
bitmap headers from disk when we read or store bitmaps would begin to
fail:

Imagine the circumstance where we've resized bitmaps in memory, but they still
have the old values on-disk. The lengths will no longer match bdrv_getlength,
so we must allow this check to be skipped when flushing bitmaps to disk.

Limit this to when we are about to overwrite the headers: we will verify the
outgoing headers, but we will skip verifying the known stale headers.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

Comments

Eric Blake March 6, 2019, 12:34 p.m. UTC | #1
On 3/5/19 5:43 PM, John Snow wrote:
> If we were to allow resizes, the length check that happens when we load
> bitmap headers from disk when we read or store bitmaps would begin to
> fail:
> 
> Imagine the circumstance where we've resized bitmaps in memory, but they still
> have the old values on-disk. The lengths will no longer match bdrv_getlength,
> so we must allow this check to be skipped when flushing bitmaps to disk.
> 
> Limit this to when we are about to overwrite the headers: we will verify the
> outgoing headers, but we will skip verifying the known stale headers.

No-op until we actually do allow resizes later in the series, but makes
sense.

> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
>  1 file changed, 21 insertions(+), 13 deletions(-)
> 
> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
> index c3b210ede1..d02730004a 100644
> --- a/block/qcow2-bitmap.c
> +++ b/block/qcow2-bitmap.c
> @@ -435,7 +435,8 @@ static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
>      return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
>  }
>  
> -static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
> +static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
> +                           bool allow_resize)
>  {
>      BDRVQcow2State *s = bs->opaque;
>      uint64_t phys_bitmap_bytes;
> @@ -462,8 +463,14 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>          return len;

Someday, it would be nice to plumb Error* through this function, so that
you can give distinct reasons for failure, rather than lumping all
failures into the nebulous "doesn't meet the constraints" because we
lost context when slamming multiple errors into a single -EINVAL. But
that's a separate patch series.

>      }
>  
> -    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
> -           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
> +    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
> +        return -EINVAL;
> +    }
> +
> +    if (!allow_resize &&
> +        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
> +        return -EINVAL;
> +    }
>  
>      return fail ? -EINVAL : 0;

Dead conditional - with your refactoring, this line is only reached when
fail == false.

With it changed to 'return 0',
Reviewed-by: Eric Blake <eblake@redhat.com>
Vladimir Sementsov-Ogievskiy March 6, 2019, 3:21 p.m. UTC | #2
06.03.2019 2:43, John Snow wrote:
> If we were to allow resizes, the length check that happens when we load
> bitmap headers from disk when we read or store bitmaps would begin to
> fail:
> 
> Imagine the circumstance where we've resized bitmaps in memory, but they still
> have the old values on-disk. The lengths will no longer match bdrv_getlength,
> so we must allow this check to be skipped when flushing bitmaps to disk.
> 
> Limit this to when we are about to overwrite the headers: we will verify the
> outgoing headers, but we will skip verifying the known stale headers.
> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>   block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
>   1 file changed, 21 insertions(+), 13 deletions(-)
> 
> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
> index c3b210ede1..d02730004a 100644
> --- a/block/qcow2-bitmap.c
> +++ b/block/qcow2-bitmap.c
> @@ -435,7 +435,8 @@ static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
>       return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
>   }
>   
> -static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
> +static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
> +                           bool allow_resize)
>   {
>       BDRVQcow2State *s = bs->opaque;
>       uint64_t phys_bitmap_bytes;
> @@ -462,8 +463,14 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>           return len;
>       }
>   
> -    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
> -           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
> +    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
> +        return -EINVAL;
> +    }
> +
> +    if (!allow_resize &&
> +        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
> +        return -EINVAL;
> +    }
>   
>       return fail ? -EINVAL : 0;
>   }
> @@ -534,7 +541,8 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
>    * checks it and convert to bitmap list.
>    */
>   static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
> -                                         uint64_t size, Error **errp)
> +                                         uint64_t size, bool allow_resize,
> +                                         Error **errp)
>   {
>       int ret;
>       BDRVQcow2State *s = bs->opaque;
> @@ -593,7 +601,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
>               goto fail;
>           }
>   
> -        ret = check_dir_entry(bs, e);
> +        ret = check_dir_entry(bs, e, allow_resize);
>           if (ret < 0) {
>               error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints",
>                          e->name_size, dir_entry_name_field(e));
> @@ -654,7 +662,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, NULL);
> +                               s->bitmap_directory_size, false, NULL);
>       if (bm_list == NULL) {
>           res->corruptions++;
>           return -EINVAL;
> @@ -755,7 +763,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
>           e->extra_data_size = 0;
>           memcpy(e + 1, bm->name, e->name_size);
>   
> -        if (check_dir_entry(bs, e) < 0) {
> +        if (check_dir_entry(bs, e, false) < 0) {
>               ret = -EINVAL;
>               goto fail;
>           }
> @@ -957,7 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return false;
>       }
> @@ -1066,7 +1074,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return NULL;
>       }
> @@ -1111,7 +1119,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return -EINVAL;
>       }
> @@ -1359,7 +1367,7 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return;
>       }
> @@ -1412,7 +1420,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>           bm_list = bitmap_list_new();
>       } else {

Worth comment here, that we assume that image may be resized?

>           bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                                   s->bitmap_directory_size, errp);
> +                                   s->bitmap_directory_size, true, errp);
>           if (bm_list == NULL) {
>               return;
>           }
> @@ -1593,7 +1601,7 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           goto fail;
>       }
> 

With Eric's suggestion:
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
John Snow March 6, 2019, 3:35 p.m. UTC | #3
On 3/6/19 10:21 AM, Vladimir Sementsov-Ogievskiy wrote:
> 06.03.2019 2:43, John Snow wrote:
>> If we were to allow resizes, the length check that happens when we load
>> bitmap headers from disk when we read or store bitmaps would begin to
>> fail:
>>
>> Imagine the circumstance where we've resized bitmaps in memory, but they still
>> have the old values on-disk. The lengths will no longer match bdrv_getlength,
>> so we must allow this check to be skipped when flushing bitmaps to disk.
>>
>> Limit this to when we are about to overwrite the headers: we will verify the
>> outgoing headers, but we will skip verifying the known stale headers.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>>   block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
>>   1 file changed, 21 insertions(+), 13 deletions(-)
>>
>> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
>> index c3b210ede1..d02730004a 100644
>> --- a/block/qcow2-bitmap.c
>> +++ b/block/qcow2-bitmap.c
>> @@ -435,7 +435,8 @@ static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
>>       return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
>>   }
>>   
>> -static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>> +static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
>> +                           bool allow_resize)
>>   {
>>       BDRVQcow2State *s = bs->opaque;
>>       uint64_t phys_bitmap_bytes;
>> @@ -462,8 +463,14 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>>           return len;
>>       }
>>   
>> -    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
>> -           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
>> +    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (!allow_resize &&
>> +        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
>> +        return -EINVAL;
>> +    }
>>   
>>       return fail ? -EINVAL : 0;
>>   }
>> @@ -534,7 +541,8 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
>>    * checks it and convert to bitmap list.
>>    */
>>   static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
>> -                                         uint64_t size, Error **errp)
>> +                                         uint64_t size, bool allow_resize,
>> +                                         Error **errp)
>>   {
>>       int ret;
>>       BDRVQcow2State *s = bs->opaque;
>> @@ -593,7 +601,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
>>               goto fail;
>>           }
>>   
>> -        ret = check_dir_entry(bs, e);
>> +        ret = check_dir_entry(bs, e, allow_resize);
>>           if (ret < 0) {
>>               error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints",
>>                          e->name_size, dir_entry_name_field(e));
>> @@ -654,7 +662,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, NULL);
>> +                               s->bitmap_directory_size, false, NULL);
>>       if (bm_list == NULL) {
>>           res->corruptions++;
>>           return -EINVAL;
>> @@ -755,7 +763,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
>>           e->extra_data_size = 0;
>>           memcpy(e + 1, bm->name, e->name_size);
>>   
>> -        if (check_dir_entry(bs, e) < 0) {
>> +        if (check_dir_entry(bs, e, false) < 0) {
>>               ret = -EINVAL;
>>               goto fail;
>>           }
>> @@ -957,7 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, errp);
>> +                               s->bitmap_directory_size, false, errp);
>>       if (bm_list == NULL) {
>>           return false;
>>       }
>> @@ -1066,7 +1074,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, errp);
>> +                               s->bitmap_directory_size, false, errp);
>>       if (bm_list == NULL) {
>>           return NULL;
>>       }
>> @@ -1111,7 +1119,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, errp);
>> +                               s->bitmap_directory_size, false, errp);
>>       if (bm_list == NULL) {
>>           return -EINVAL;
>>       }
>> @@ -1359,7 +1367,7 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, errp);
>> +                               s->bitmap_directory_size, false, errp);
>>       if (bm_list == NULL) {
>>           return;
>>       }
>> @@ -1412,7 +1420,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>>           bm_list = bitmap_list_new();
>>       } else {
> 
> Worth comment here, that we assume that image may be resized?
> 

You mean below, to explain the 'true' parameter? Yes, I will do so.

>>           bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                                   s->bitmap_directory_size, errp);
>> +                                   s->bitmap_directory_size, true, errp);
>>           if (bm_list == NULL) {
>>               return;
>>           }
>> @@ -1593,7 +1601,7 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
>>       }
>>   
>>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
>> -                               s->bitmap_directory_size, errp);
>> +                               s->bitmap_directory_size, false, errp);
>>       if (bm_list == NULL) {
>>           goto fail;
>>       }
>>
> 
> With Eric's suggestion:
> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> 

The bad return conditional, I assume? OK, will do.
Vladimir Sementsov-Ogievskiy March 6, 2019, 4:07 p.m. UTC | #4
06.03.2019 2:43, John Snow wrote:
> If we were to allow resizes, the length check that happens when we load
> bitmap headers from disk when we read or store bitmaps would begin to
> fail:
> 
> Imagine the circumstance where we've resized bitmaps in memory, but they still
> have the old values on-disk. The lengths will no longer match bdrv_getlength,
> so we must allow this check to be skipped when flushing bitmaps to disk.
> 
> Limit this to when we are about to overwrite the headers: we will verify the
> outgoing headers, but we will skip verifying the known stale headers.
> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>   block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
>   1 file changed, 21 insertions(+), 13 deletions(-)
> 
> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
> index c3b210ede1..d02730004a 100644
> --- a/block/qcow2-bitmap.c
> +++ b/block/qcow2-bitmap.c
> @@ -435,7 +435,8 @@ static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
>       return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
>   }
>   
> -static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
> +static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
> +                           bool allow_resize)
>   {
>       BDRVQcow2State *s = bs->opaque;
>       uint64_t phys_bitmap_bytes;
> @@ -462,8 +463,14 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>           return len;
>       }
>   
> -    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
> -           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
> +    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
> +        return -EINVAL;
> +    }
> +
> +    if (!allow_resize &&
> +        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
> +        return -EINVAL;
> +    }

now I think, that we don't need additional parameter, but instead do this check only if
! entry->flags & BME_FLAG_IN_USE, which will correspond to:

1. incoming: bitmap loaded from image
2. outgoing: bitmap stored to the image

>   
>       return fail ? -EINVAL : 0;
>   }
> @@ -534,7 +541,8 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
>    * checks it and convert to bitmap list.
>    */
>   static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
> -                                         uint64_t size, Error **errp)
> +                                         uint64_t size, bool allow_resize,
> +                                         Error **errp)
>   {
>       int ret;
>       BDRVQcow2State *s = bs->opaque;
> @@ -593,7 +601,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
>               goto fail;
>           }
>   
> -        ret = check_dir_entry(bs, e);
> +        ret = check_dir_entry(bs, e, allow_resize);
>           if (ret < 0) {
>               error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints",
>                          e->name_size, dir_entry_name_field(e));
> @@ -654,7 +662,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, NULL);
> +                               s->bitmap_directory_size, false, NULL);
>       if (bm_list == NULL) {
>           res->corruptions++;
>           return -EINVAL;
> @@ -755,7 +763,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
>           e->extra_data_size = 0;
>           memcpy(e + 1, bm->name, e->name_size);
>   
> -        if (check_dir_entry(bs, e) < 0) {
> +        if (check_dir_entry(bs, e, false) < 0) {
>               ret = -EINVAL;
>               goto fail;
>           }
> @@ -957,7 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return false;
>       }
> @@ -1066,7 +1074,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return NULL;
>       }
> @@ -1111,7 +1119,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return -EINVAL;
>       }
> @@ -1359,7 +1367,7 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           return;
>       }
> @@ -1412,7 +1420,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>           bm_list = bitmap_list_new();
>       } else {
>           bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                                   s->bitmap_directory_size, errp);
> +                                   s->bitmap_directory_size, true, errp);
>           if (bm_list == NULL) {
>               return;
>           }
> @@ -1593,7 +1601,7 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
>       }
>   
>       bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
> -                               s->bitmap_directory_size, errp);
> +                               s->bitmap_directory_size, false, errp);
>       if (bm_list == NULL) {
>           goto fail;
>       }
>
John Snow March 8, 2019, 10:10 p.m. UTC | #5
On 3/6/19 11:07 AM, Vladimir Sementsov-Ogievskiy wrote:
> 06.03.2019 2:43, John Snow wrote:
>> If we were to allow resizes, the length check that happens when we load
>> bitmap headers from disk when we read or store bitmaps would begin to
>> fail:
>>
>> Imagine the circumstance where we've resized bitmaps in memory, but they still
>> have the old values on-disk. The lengths will no longer match bdrv_getlength,
>> so we must allow this check to be skipped when flushing bitmaps to disk.
>>
>> Limit this to when we are about to overwrite the headers: we will verify the
>> outgoing headers, but we will skip verifying the known stale headers.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>>   block/qcow2-bitmap.c | 34 +++++++++++++++++++++-------------
>>   1 file changed, 21 insertions(+), 13 deletions(-)
>>
>> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
>> index c3b210ede1..d02730004a 100644
>> --- a/block/qcow2-bitmap.c
>> +++ b/block/qcow2-bitmap.c
>> @@ -435,7 +435,8 @@ static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
>>       return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
>>   }
>>   
>> -static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>> +static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
>> +                           bool allow_resize)
>>   {
>>       BDRVQcow2State *s = bs->opaque;
>>       uint64_t phys_bitmap_bytes;
>> @@ -462,8 +463,14 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
>>           return len;
>>       }
>>   
>> -    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
>> -           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
>> +    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (!allow_resize &&
>> +        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
>> +        return -EINVAL;
>> +    }
> 
> now I think, that we don't need additional parameter, but instead do this check only if
> ! entry->flags & BME_FLAG_IN_USE, which will correspond to:
> 
> 1. incoming: bitmap loaded from image
> 2. outgoing: bitmap stored to the image
> 

I can't tell if I like this or not, because we'd skip checking the image
on load if it was improperly stored. I guess that's... fine.

Though, it would mean we also skip the consistency check on subsequent
re-reads of the bitmap list, which I think we still want if only as a
sanity check on the code, right?

Further thoughts?

(Maybe this portion of the code is due for a refactor in 4.1 so we can
add better error messages to it, like Eric suggests.)
diff mbox series

Patch

diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index c3b210ede1..d02730004a 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -435,7 +435,8 @@  static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry)
     return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry));
 }
 
-static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
+static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry,
+                           bool allow_resize)
 {
     BDRVQcow2State *s = bs->opaque;
     uint64_t phys_bitmap_bytes;
@@ -462,8 +463,14 @@  static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
         return len;
     }
 
-    fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
-           (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
+    if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
+        return -EINVAL;
+    }
+
+    if (!allow_resize &&
+        (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) {
+        return -EINVAL;
+    }
 
     return fail ? -EINVAL : 0;
 }
@@ -534,7 +541,8 @@  static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
  * checks it and convert to bitmap list.
  */
 static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
-                                         uint64_t size, Error **errp)
+                                         uint64_t size, bool allow_resize,
+                                         Error **errp)
 {
     int ret;
     BDRVQcow2State *s = bs->opaque;
@@ -593,7 +601,7 @@  static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
             goto fail;
         }
 
-        ret = check_dir_entry(bs, e);
+        ret = check_dir_entry(bs, e, allow_resize);
         if (ret < 0) {
             error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints",
                        e->name_size, dir_entry_name_field(e));
@@ -654,7 +662,7 @@  int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, NULL);
+                               s->bitmap_directory_size, false, NULL);
     if (bm_list == NULL) {
         res->corruptions++;
         return -EINVAL;
@@ -755,7 +763,7 @@  static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
         e->extra_data_size = 0;
         memcpy(e + 1, bm->name, e->name_size);
 
-        if (check_dir_entry(bs, e) < 0) {
+        if (check_dir_entry(bs, e, false) < 0) {
             ret = -EINVAL;
             goto fail;
         }
@@ -957,7 +965,7 @@  bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+                               s->bitmap_directory_size, false, errp);
     if (bm_list == NULL) {
         return false;
     }
@@ -1066,7 +1074,7 @@  Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+                               s->bitmap_directory_size, false, errp);
     if (bm_list == NULL) {
         return NULL;
     }
@@ -1111,7 +1119,7 @@  int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+                               s->bitmap_directory_size, false, errp);
     if (bm_list == NULL) {
         return -EINVAL;
     }
@@ -1359,7 +1367,7 @@  void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+                               s->bitmap_directory_size, false, errp);
     if (bm_list == NULL) {
         return;
     }
@@ -1412,7 +1420,7 @@  void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
         bm_list = bitmap_list_new();
     } else {
         bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                                   s->bitmap_directory_size, errp);
+                                   s->bitmap_directory_size, true, errp);
         if (bm_list == NULL) {
             return;
         }
@@ -1593,7 +1601,7 @@  bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
     }
 
     bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+                               s->bitmap_directory_size, false, errp);
     if (bm_list == NULL) {
         goto fail;
     }