Michal Privoznik via Devel wrote:

> 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


Reviewed-by: Roman Bogorodskiy <[email protected]>

Reply via email to