CVE-2018-7254: Out-of-bounds Read (CWE-125)
value: The ParseCaffHeaderConfig function of the cli/caff.c file of WavPack 5.1.0 allows a remote attacker to cause a denial-of-service (global buffer over-read), or possibly trigger a buffer overflow or incorrect memory allocation, via a maliciously crafted CAF file.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.8
Published: 2018-02-19T23:29Z
Repository: https://github.com/dbry/WavPack
Language: C
Files changed: cli/caff.c
Commit date: 2018-02-11 16:37:47 -0800
Commit message: issue #28, fix buffer overflows and bad allocs on corrupt CAF files

Diff: diff --git a/cli/caff.c b/cli/caff.c
index ae57c4b..6248a71 100644
--- a/cli/caff.c
+++ b/cli/caff.c
@@ -87,12 +87,12 @@ typedef struct
     float mCoordinates [3];
 } CAFChannelDescription;
 
 #define CAFChannelDescriptionFormat "LLLLL"
 
-static const char TMH_full [] = { 1,2,3,13,9,10,5,6,12,14,15,16,17,9,4,18,7,8,19,20,21 };
-static const char TMH_std [] = { 1,2,3,11,8,9,5,6,10,12,13,14,15,7,4,16 };
+static const char TMH_full [] = { 1,2,3,13,9,10,5,6,12,14,15,16,17,9,4,18,7,8,19,20,21,0 };
+static const char TMH_std [] = { 1,2,3,11,8,9,5,6,10,12,13,14,15,7,4,16,0 };
 
 static struct {
     uint32_t mChannelLayoutTag;     // Core Audio layout, 100 - 146 in high word, num channels in low word
     uint32_t mChannelBitmap;        // Microsoft standard mask (for those channels that appear)
     const char *mChannelReorder;    // reorder string if layout is NOT in Microsoft standard order
@@ -272,14 +272,23 @@ int ParseCaffHeaderConfig (FILE *infile, char *infilename, char *fourcc, Wavpack
                     error_line ("data format: %d-bit %s-endian integers stored in %d byte(s)",
                         config->bits_per_sample, (config->qmode & QMODE_BIG_ENDIAN) ? "big" : "little", config->bytes_per_sample);
             }
         }
         else if (!strncmp (caf_chunk_header.mChunkType, "chan", 4)) {
-            CAFChannelLayout *caf_channel_layout = malloc ((size_t) caf_chunk_header.mChunkSize);
+            CAFChannelLayout *caf_channel_layout;
 
-            if (caf_chunk_header.mChunkSize < sizeof (CAFChannelLayout) ||
-                !DoReadFile (infile, caf_channel_layout, (uint32_t) caf_chunk_header.mChunkSize, &bcount) ||
+            if (caf_chunk_header.mChunkSize < sizeof (CAFChannelLayout) || caf_chunk_header.mChunkSize > 1024) {
+                error_line ("this .CAF file has an invalid 'chan' chunk!");
+                return WAVPACK_SOFT_ERROR;
+            }
+
+            if (debug_logging_mode)
+                error_line ("'chan' chunk is %d bytes", (int) caf_chunk_header.mChunkSize);
+
+            caf_channel_layout = malloc ((size_t) caf_chunk_header.mChunkSize);
+
+            if (!DoReadFile (infile, caf_channel_layout, (uint32_t) caf_chunk_header.mChunkSize, &bcount) ||
                 bcount != caf_chunk_header.mChunkSize) {
                     error_line ("%s is not a valid .CAF file!", infilename);
                     free (caf_channel_layout);
                     return WAVPACK_SOFT_ERROR;
             }
@@ -493,12 +502,19 @@ int ParseCaffHeaderConfig (FILE *infile, char *infilename, char *fourcc, Wavpack
 
             break;
         }
         else {          // just copy unknown chunks to output file
 
-            int bytes_to_copy = (uint32_t) caf_chunk_header.mChunkSize;
-            char *buff = malloc (bytes_to_copy);
+            uint32_t bytes_to_copy = (uint32_t) caf_chunk_header.mChunkSize;
+            char *buff;
+
+            if (caf_chunk_header.mChunkSize < 0 || caf_chunk_header.mChunkSize > 1048576) {
+                error_line ("%s is not a valid .CAF file!", infilename);
+                return WAVPACK_SOFT_ERROR;
+            }
+
+            buff = malloc (bytes_to_copy);
 
             if (debug_logging_mode)
                 error_line ("extra unknown chunk \"%c%c%c%c\" of %d bytes",
                     caf_chunk_header.mChunkType [0], caf_chunk_header.mChunkType [1], caf_chunk_header.mChunkType [2],
                     caf_chunk_header.mChunkType [3], caf_chunk_header.mChunkSize);

Vulnerable code: static const char TMH_full [] = { 1,2,3,13,9,10,5,6,12,14,15,16,17,9,4,18,7,8,19,20,21 };
static const char TMH_std [] = { 1,2,3,11,8,9,5,6,10,12,13,14,15,7,4,16 };
            CAFChannelLayout *caf_channel_layout = malloc ((size_t) caf_chunk_header.mChunkSize);
            if (caf_chunk_header.mChunkSize < sizeof (CAFChannelLayout) ||
                !DoReadFile (infile, caf_channel_layout, (uint32_t) caf_chunk_header.mChunkSize, &bcount) ||
            int bytes_to_copy = (uint32_t) caf_chunk_header.mChunkSize;
            char *buff = malloc (bytes_to_copy);
Fixed code: static const char TMH_full [] = { 1,2,3,13,9,10,5,6,12,14,15,16,17,9,4,18,7,8,19,20,21,0 };
static const char TMH_std [] = { 1,2,3,11,8,9,5,6,10,12,13,14,15,7,4,16,0 };
            CAFChannelLayout *caf_channel_layout;
            if (caf_chunk_header.mChunkSize < sizeof (CAFChannelLayout) || caf_chunk_header.mChunkSize > 1024) {
                error_line ("this .CAF file has an invalid 'chan' chunk!");
                return WAVPACK_SOFT_ERROR;
            }

            if (debug_logging_mode)
                error_line ("'chan' chunk is %d bytes", (int) caf_chunk_header.mChunkSize);

            caf_channel_layout = malloc ((size_t) caf_chunk_header.mChunkSize);

            if (!DoReadFile (infile, caf_channel_layout, (uint32_t) caf_chunk_header.mChunkSize, &bcount) ||
            uint32_t bytes_to_copy = (uint32_t) caf_chunk_header.mChunkSize;
            char *buff;

            if (caf_chunk_header.mChunkSize < 0 || caf_chunk_header.mChunkSize > 1048576) {
                error_line ("%s is not a valid .CAF file!", infilename);
                return WAVPACK_SOFT_ERROR;
            }

            buff = malloc (bytes_to_copy);