CVE-2020-29367: Out-of-bounds Write (CWE-787)
value: blosc2.c in Blosc C-Blosc2 through 2.0.0.beta.5 has a heap-based buffer overflow when there is a lack of space to write compressed data.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.8
Published: 2020-11-27T20:15Z
Repository: https://github.com/Blosc/c-blosc2
Language: C
Files changed: blosc/blosc2.c
Commit date: 2020-10-17 16:44:03 -0700
Commit message: Fixed asan heap buffer overflow when not enough space to write compressed block size.

Diff: diff --git a/blosc/blosc2.c b/blosc/blosc2.c
index 5b1c3ac..3f35b5a 100644
--- a/blosc/blosc2.c
+++ b/blosc/blosc2.c
@@ -704,11 +704,11 @@ static bool get_run(const uint8_t* ip, const uint8_t* ip_bound) {
 }
 
 
 /* Shuffle & compress a single block */
 static int blosc_c(struct thread_context* thread_context, int32_t bsize,
-                   int32_t leftoverblock, int32_t ntbytes, int32_t maxbytes,
+                   int32_t leftoverblock, int32_t ntbytes, int32_t destsize,
                    const uint8_t* src, const int32_t offset, uint8_t* dest,
                    uint8_t* tmp, uint8_t* tmp2) {
   blosc2_context* context = thread_context->parent_context;
   int dont_split = (context->header_flags & 0x10) >> 4;
   int dict_training = context->use_dict && context->dict_cdict == NULL;
@@ -770,23 +770,27 @@ static int blosc_c(struct thread_context* thread_context, int32_t bsize,
     const uint8_t* ip = (uint8_t*)_src + j * neblock;
     const uint8_t* ipbound = (uint8_t*)_src + (j + 1) * neblock;
     if (get_run(ip, ipbound)) {
       // A run.  Encode the repeated byte as a negative length in the length of the split.
       int32_t value = _src[j * neblock];
+      if (ntbytes > destsize) {
+        /* Not enough space to write out compressed block size */
+        return -1;
+      }
       _sw32(dest - 4, -value);
       continue;
     }
 
     maxout = neblock;
   #if defined(HAVE_SNAPPY)
     if (context->compcode == BLOSC_SNAPPY) {
       maxout = (int32_t)snappy_max_compressed_length((size_t)neblock);
     }
   #endif /*  HAVE_SNAPPY */
-    if (ntbytes + maxout > maxbytes) {
+    if (ntbytes + maxout > destsize) {
       /* avoid buffer * overrun */
-      maxout = (int64_t)maxbytes - (int64_t)ntbytes;
+      maxout = (int64_t)destsize - (int64_t)ntbytes;
       if (maxout <= 0) {
         return 0;                  /* non-compressible block */
       }
     }
     if (dict_training) {
@@ -857,11 +861,11 @@ static int blosc_c(struct thread_context* thread_context, int32_t bsize,
     if (!dict_training) {
       if (cbytes == 0 || cbytes == neblock) {
         /* The compressor has been unable to compress data at all. */
         /* Before doing the copy, check that we are not running into a
            buffer overflow. */
-        if ((ntbytes + neblock) > maxbytes) {
+        if ((ntbytes + neblock) > destsize) {
           return 0;    /* Non-compressible data */
         }
         memcpy(dest, _src + j * neblock, (unsigned int)neblock);
         cbytes = neblock;
       }

Vulnerable code:                    int32_t leftoverblock, int32_t ntbytes, int32_t maxbytes,
    if (ntbytes + maxout > maxbytes) {
      maxout = (int64_t)maxbytes - (int64_t)ntbytes;
        if ((ntbytes + neblock) > maxbytes) {
Fixed code:                    int32_t leftoverblock, int32_t ntbytes, int32_t destsize,
      if (ntbytes > destsize) {
        /* Not enough space to write out compressed block size */
        return -1;
      }
    if (ntbytes + maxout > destsize) {
      maxout = (int64_t)destsize - (int64_t)ntbytes;
        if ((ntbytes + neblock) > destsize) {