From: Michal Privoznik <[email protected]>

Ever since of v6.9.0-rc1~384 we allow zero sized bitmaps. If
that's the case then the map is kept unallocated (NULL).
Unfortunately, some virBitmap APIs call memcpy()/memset() which
doesn't allow NULL pointers. Introduce checks to these APIs
(virBitmapNewCopy(), virBitmapSetAll() and virBitmapClearAll()).
While at it, also introduce a test case to virbitmaptest to catch
UB in our CI.

Signed-off-by: Michal Privoznik <[email protected]>
---
 src/util/virbitmap.c  |  9 ++++++++-
 tests/virbitmaptest.c | 27 +++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/util/virbitmap.c b/src/util/virbitmap.c
index 78545501b7..b9b737a087 100644
--- a/src/util/virbitmap.c
+++ b/src/util/virbitmap.c
@@ -587,7 +587,8 @@ virBitmapNewCopy(virBitmap *src)
 {
     virBitmap *dst = virBitmapNew(src->nbits);
 
-    memcpy(dst->map, src->map, dst->map_len * sizeof(src->map[0]));
+    if (G_LIKELY(dst->map_len > 0))
+        memcpy(dst->map, src->map, dst->map_len * sizeof(src->map[0]));
 
     return dst;
 }
@@ -770,6 +771,9 @@ virBitmapClearTail(virBitmap *bitmap)
  */
 void virBitmapSetAll(virBitmap *bitmap)
 {
+    if (G_UNLIKELY(bitmap->map_len == 0))
+        return;
+
     memset(bitmap->map, 0xff,
            bitmap->map_len * (VIR_BITMAP_BITS_PER_UNIT / CHAR_BIT));
 
@@ -786,6 +790,9 @@ void virBitmapSetAll(virBitmap *bitmap)
 void
 virBitmapClearAll(virBitmap *bitmap)
 {
+    if (G_UNLIKELY(bitmap->map_len == 0))
+        return;
+
     memset(bitmap->map, 0,
            bitmap->map_len * (VIR_BITMAP_BITS_PER_UNIT / CHAR_BIT));
 }
diff --git a/tests/virbitmaptest.c b/tests/virbitmaptest.c
index 709c62ab54..549956ad04 100644
--- a/tests/virbitmaptest.c
+++ b/tests/virbitmaptest.c
@@ -768,6 +768,30 @@ test17(const void *opaque G_GNUC_UNUSED)
 }
 
 
+static int
+test18(const void *opaque G_GNUC_UNUSED)
+{
+    g_autoptr(virBitmap) map = virBitmapNew(0);
+    g_autoptr(virBitmap) copy = virBitmapNewCopy(map);
+
+    /* Creating an empty bitmap means underlying array of bits is
+     * unallocated (NULL). For some virBitmap APIs this may lead
+     * to UB. The whole point of this test is to call those and
+     * rely on clang's UB sanitizer to catch such errors at
+     * runtime. */
+
+    virBitmapSetAll(map);
+    virBitmapClearAll(copy);
+
+    if (!virBitmapEqual(map, copy)) {
+        fprintf(stderr, "empty maps don't equal\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+
 #define TESTBINARYOP(A, B, RES, FUNC) \
     testBinaryOpData.a = A; \
     testBinaryOpData.b = B; \
@@ -847,6 +871,9 @@ mymain(void)
     if (virTestRun("test17", test17, NULL) < 0)
         ret = -1;
 
+    if (virTestRun("test18", test18, NULL) < 0)
+        ret = -1;
+
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-- 
2.55.0

Reply via email to