The following commands:
nbdkit -fv --filter=cow memory size=512 --run 'qemu-img info $nbd'
nbdkit -fv --filter=cache memory size=512 --run 'qemu-img info $nbd'
nbdkit -fv --filter=cow null --run 'qemu-img info $nbd'
all fail with:
nbdkit: memory[1]: error: realloc: Success
Initial git bisect pointed to commit 3166d2bcbfd2 (this is not real
cause, it merely exposes the bug).
The reason this happens is because the new behaviour after
commit 3166d2bcbfd2 is to round down the size of the underlying disk
to the cow/cache filter block size.
=> Size of the underlying disk is 512 or 0 bytes,
block size is 4096 bytes.
=> Size of the disk is rounded down to 0.
=> The cow/cache filter requests a bitmap of size 0.
=> bitmap_resize calls realloc (ptr, 0).
=> The glibc implementation of realloc returns NULL + errno == 0.
(Other realloc implementations can behave differently.)
=> This is not an error, but the existing code thinks it is because of
the NULL return.
This commit changes the code so it doesn't bother to call realloc if
the new bitmap size would be 0.
There are many other places in nbdkit where we call realloc, and I did
not vet any of them to see if similar bugs could be present, but it is
quite likely.
Note in passing that the correct way to use the cow/cache filter with
a disk which isn't a multiple of the block size is to combine it with
the truncate filter, eg:
nbdkit -fv --filter=cow --filter=truncate memory size=512 round-up=4096
Thanks: Eric Blake
---
common/bitmap/bitmap.c | 14 ++++++++++----
tests/Makefile.am | 2 ++
tests/test-cow-null.sh | 42 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/common/bitmap/bitmap.c b/common/bitmap/bitmap.c
index caac916..2cc625c 100644
--- a/common/bitmap/bitmap.c
+++ b/common/bitmap/bitmap.c
@@ -59,10 +59,16 @@ bitmap_resize (struct bitmap *bm, uint64_t new_size)
}
new_bm_size = (size_t) new_bm_size_u64;
- new_bitmap = realloc (bm->bitmap, new_bm_size);
- if (new_bitmap == NULL) {
- nbdkit_error ("realloc: %m");
- return -1;
+ if (new_bm_size > 0) {
+ new_bitmap = realloc (bm->bitmap, new_bm_size);
+ if (new_bitmap == NULL) {
+ nbdkit_error ("realloc: %m");
+ return -1;
+ }
+ }
+ else {
+ free (bm->bitmap);
+ new_bitmap = NULL;
}
bm->bitmap = new_bitmap;
bm->size = new_bm_size;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f54597b..9eec75e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,6 +50,7 @@ EXTRA_DIST = \
test-cacheextents.sh \
test-captive.sh \
test-cow.sh \
+ test-cow-null.sh \
test-cxx.sh \
test-data-7E.sh \
test-data-base64.sh \
@@ -960,6 +961,7 @@ TESTS += test-cacheextents.sh
if HAVE_GUESTFISH
TESTS += test-cow.sh
endif HAVE_GUESTFISH
+TESTS += test-cow-null.sh
# delay filter tests.
TESTS += test-shutdown.sh
diff --git a/tests/test-cow-null.sh b/tests/test-cow-null.sh
new file mode 100755
index 0000000..fd6c04f
--- /dev/null
+++ b/tests/test-cow-null.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2019 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Regression test for placing a cow filter over a < 4K sized device,
+# which was broken in nbdkit 1.14.0.
+
+source ./functions.sh
+set -e
+set -x
+
+requires qemu-img --version
+
+nbdkit -fv --filter=cow null --run 'qemu-img info $nbd'
--
2.23.0