Skip to contentSkip to conversation

migration: Appease coverity, skip empty block trees

PATCH In review John Snow 3 participants 6 messages Jun 22, 2018 – Jun 29, 2018

lore
Share & tools
lore

John Snow

author
#0 lore raw

PATCH 1/1 migration: Appease coverity, skip empty block trees

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
migration/block-dirty-bitmap.c +4 -0
index 3bafbbdc4c..02725293dd 100644
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;
+        }
 
         for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
              bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
Signature · 1 line
2.14.4
#1 lore raw
Quoted 7 lines · John Snow

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

Well... with your explanation, your patch is correct.
But do we really want to maintain a 'debug with assert' vs 'production without assertions' codebase?

$ git grep g_assert_not_reached | egrep -v '^tests/' | wc -l
406

Does Coverity require all these 406 lines to behave with a "configuration [that] should not exist in practice"?

Quoted 11 lines
---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
migration/block-dirty-bitmap.c +0 -0
index 3bafbbdc4c..02725293dd 100644

migration/block-dirty-bitmap.c static int init_dirty_bitmap_migration(void) · line 292

Quoted 6 lines
migration/block-dirty-bitmap.c +3 -0
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;

Can we choose one or the other?

Quoted 5 lines
migration/block-dirty-bitmap.c +1 -0
+        }
 
         for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
              bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
author
#2 lore raw
Quoted 5 lines · Philippe Mathieu-Daudé
Quoted 4 lines · John Snow

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

Quoted 8 lines
Quoted 3 lines

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

Well... with your explanation, your patch is correct.
But do we really want to maintain a 'debug with assert' vs 'production without assertions' codebase?

I might have misremembered, but I seem to recall that it is literally possible to disable glib assertions. We endeavor not to, but it's *possible*.

In this case I'd prefer to have a runtime behavior that makes sense:
skipping such trees is sensible because they won't have any bitmaps for us to migrate.

However, this is a really bizarre block configuration that I suspect cannot exist in practice. If it does, I'd rather not ignore it in the development context.

Quoted 12 lines
$ git grep g_assert_not_reached | egrep -v '^tests/' | wc -l
406

Does Coverity require all these 406 lines to behave with a "configuration [that] should not exist in practice"?

Quoted 6 lines
---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
Quoted 14 lines

migration/block-dirty-bitmap.c static int init_dirty_bitmap_migration(void) · line 292

Quoted 11 lines
migration/block-dirty-bitmap.c +3 -0
index 3bafbbdc4c..02725293dd 100644
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;

Can we choose one or the other?

Ask someone more familiar with assert policy and a fondness for paint hues.

--js

Quoted 5 lines
Quoted 5 lines
migration/block-dirty-bitmap.c +1 -0
+        }
 
         for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
              bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
#3 lore raw
Quoted 10 lines · John Snow
Quoted 9 lines · Philippe Mathieu-Daudé
Quoted 7 lines · John Snow

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

Quoted 8 lines
Quoted 4 lines

Well... with your explanation, your patch is correct.
But do we really want to maintain a 'debug with assert' vs 'production without assertions' codebase?

I might have misremembered, but I seem to recall that it is literally possible to disable glib assertions. We endeavor not to, but it's *possible*.

There was a thread once [1] which resulted in 262a69f4282:

commit 262a69f4282e44426c7a132138581d400053e0a1

Author: Eric Blake <[email protected]>
Date:   Mon Sep 11 16:13:20 2017 -0500

osdep.h: Prohibit disabling assert() in supported builds

We already have several files that knowingly require assert() to work, sometimes because refactoring the code for proper error handling has not been tackled yet; there are probably other files that have a similar situation but with no comments documenting the same. In fact, we have places in migration that handle untrusted input with assertions, where disabling the assertions risks a worse security hole than the current behavior of losing the guest to SIGABRT when migration fails because of the assertion. Promote our current per-file safety-valve to instead be project-wide, and expand it to also cover glib's g_assert().

Note that we do NOT want to encourage 'assert(side-effects);' (that is a bad practice that prevents copy-and-paste of code to other projects that CAN disable assertions; plus it costs unnecessary reviewer mental cycles to remember whether a project special-cases the crippling of asserts); and we would LIKE to fix migration to not rely on asserts (but that takes a big code audit). But in the meantime, we DO want to send a message that anyone that disables assertions has to tweak code in order to compile, making it obvious that they are taking on additional risk that we are not going to support. At the same time, leave comments mentioning NDEBUG in files that we know still need to be scrubbed, so there is at least something to grep for.

It would be possible to come up with some other mechanism for doing runtime checking by default, but which does not abort the program on failure, while leaving side effects in place (unlike how crippling assert() avoids even the side effects), perhaps under the name q_verify(); but it was not deemed worth the effort (developers should not have to learn a replacement when the standard C macro works just fine, and it would be a lot of churn for little gain). The patch specifically uses #error rather than #warn so that a user is forced to tweak the header to acknowledge the issue, even when not using a -Werror compilation.

which added in include/qemu/osdep.h:

/*
* We have a lot of unaudited code that may fail in strange ways, or
* even be a security risk during migration, if you disable assertions
* at compile-time. You may comment out these safety checks if you
* absolutely want to disable assertion overhead, but it is not
* supported upstream so the risk is all yours. Meanwhile, please
* submit patches to remove any side-effects inside an assertion, or
* fixing error handling that should use Error instead of assert. */
#ifdef NDEBUG
#error building with NDEBUG is not supported
#endif
#ifdef G_DISABLE_ASSERT
#error building with G_DISABLE_ASSERT is not supported #endif

So this shouldn't be a problem.

[1] http://lists.nongnu.org/archive/html/qemu-devel/2017-08/msg03608.html

Quoted 44 lines

In this case I'd prefer to have a runtime behavior that makes sense:
skipping such trees is sensible because they won't have any bitmaps for us to migrate.

However, this is a really bizarre block configuration that I suspect cannot exist in practice. If it does, I'd rather not ignore it in the development context.

Quoted 12 lines
$ git grep g_assert_not_reached | egrep -v '^tests/' | wc -l
406

Does Coverity require all these 406 lines to behave with a "configuration [that] should not exist in practice"?

Quoted 6 lines
---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
Quoted 14 lines

migration/block-dirty-bitmap.c static int init_dirty_bitmap_migration(void) · line 292

Quoted 11 lines
migration/block-dirty-bitmap.c +3 -0
index 3bafbbdc4c..02725293dd 100644
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;

Can we choose one or the other?

Ask someone more familiar with assert policy and a fondness for paint hues.

--js

Quoted 5 lines
Quoted 5 lines
migration/block-dirty-bitmap.c +1 -0
+        }
 
         for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
              bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
#4 lore raw
In reply to John Snow
Quoted 18 lines · John Snow

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
migration/block-dirty-bitmap.c +0 -0
index 3bafbbdc4c..02725293dd 100644

migration/block-dirty-bitmap.c static int init_dirty_bitmap_migration(void) · line 293

Quoted 7 lines
migration/block-dirty-bitmap.c +4 -0
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;
+        }

If bs can never be NULL, why test that it is non-NULL in the while loop condition?

Try:

  /* Precondition: bs != NULL thanks to the for loop */
  while (bs->drv && bs->implicit) {
      bs = backing_bs(bs);
  }
  /* Postcondition: bs != NULL due to implicit node layout assumption */

Does this silence Coverity? ISTR it looks for cues like the bs check in the while loop condition to decide whether it's likely that a variable could be NULL.

author
#5 lore raw
Quoted 38 lines · Stefan Hajnoczi
Quoted 18 lines · John Snow

If a tree consists exclusively of implicit filter nodes, we might crash QEMU. This configuration should not exist in practice, but if it did, skipping it would be fine.

For the purposes of debug builds, throw an assert to remind us that this configuration is truly unexpected, but if it's compiled out we will cope just fine.

---
 migration/block-dirty-bitmap.c | 4 ++++
 1 file changed, 4 insertions(+)
migration/block-dirty-bitmap.c +0 -0
index 3bafbbdc4c..02725293dd 100644

migration/block-dirty-bitmap.c static int init_dirty_bitmap_migration(void) · line 293

Quoted 7 lines
migration/block-dirty-bitmap.c +4 -0
@@ -287,6 +287,10 @@ static int init_dirty_bitmap_migration(void)
         while (bs && bs->drv && bs->implicit) {
             bs = backing_bs(bs);
         }
+        if (!bs) {
+            g_assert_not_reached();
+            continue;
+        }

If bs can never be NULL, why test that it is non-NULL in the while loop condition?

Try:

  /* Precondition: bs != NULL thanks to the for loop */
  while (bs->drv && bs->implicit) {
      bs = backing_bs(bs);
  }
  /* Postcondition: bs != NULL due to implicit node layout assumption */
Quoted 4 lines

Does this silence Coverity? ISTR it looks for cues like the bs check in the while loop condition to decide whether it's likely that a variable could be NULL.

I'll give this a go, but mechanically it looks suspect without an assert(bs) in the loop body, but that would definitely silence Coverity.

--js