Commit message: Merge two enforces into one.
CVE: CVE-2019-13112
CWE: CWE-770
Vulnerability description: A PngChunk::parseChunkContent uncontrolled memory allocation in Exiv2 through 0.27.1 allows an attacker to cause a denial of service (crash due to an std::bad_alloc exception) via a crafted PNG image file.

Project: exiv2
File: pngchunk_int.cpp

    DataBuf PngChunk::readRawProfile(const DataBuf& text,bool iTXt)
    {
        DataBuf                 info;
        unsigned char           unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
            13,14,15};
        if (text.size_ == 0) {
            return DataBuf();
        }

        if ( iTXt ) {
            info.alloc(text.size_);
            ::memcpy(info.pData_,text.pData_,text.size_);
            return  info;
        }

        const char *sp  = (char*) text.pData_+1;          // current byte (space pointer)
        const char *eot = (char*) text.pData_+text.size_; // end of text

        if (sp >= eot) {
            return DataBuf();
        }

        // Look for newline
        while (*sp != '\n')
        {
            sp++;
            if ( sp == eot )
            {
                return DataBuf();
            }
        }
        sp++ ; // step over '\n'
        if (sp == eot) {
            return DataBuf();
        }

        // Look for length
        while (*sp == '\0' || *sp == ' ' || *sp == '\n')
        {
            sp++;
            if (sp == eot )
            {
                return DataBuf();
            }
        }

        const char* startOfLength = sp;
        while ('0' <= *sp && *sp <= '9')
        {
            sp++;
            if (sp == eot )
            {
                return DataBuf();
            }
        }
        sp++ ; // step over '\n'
        if (sp == eot) {
            return DataBuf();
        }

        long length = (long) atol(startOfLength);
        enforce(0 <= length && length <= (eot - sp)/2, Exiv2::kerCorruptedMetadata);

        // Allocate space
        if (length == 0)
        {
#ifdef DEBUG
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: invalid profile length\n";
#endif
        }
        info.alloc(length);
        if (info.size_ != length)
        {
#ifdef DEBUG
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: cannot allocate memory\n";
#endif
            return DataBuf();
        }

        // Copy profile, skipping white space and column 1 "=" signs

        unsigned char *dp = (unsigned char*)info.pData_; // decode pointer
        unsigned int nibbles = length * 2;

        for (long i = 0; i < (long) nibbles; i++)
        {
            enforce(sp < eot, Exiv2::kerCorruptedMetadata);
            while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
            {
                if (*sp == '\0')
                {
#ifdef DEBUG
                    std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: ran out of data\n";
#endif
                    return DataBuf();
                }

                sp++;
                enforce(sp < eot, Exiv2::kerCorruptedMetadata);
            }

            if (i%2 == 0)
                *dp = (unsigned char) (16*unhex[(int) *sp++]);
            else
                (*dp++) += unhex[(int) *sp++];
        }

        return info;

    } // PngChunk::readRawProfile

Project: exiv2
File: pngchunk_int.cpp

    DataBuf PngChunk::parseTXTChunk(const DataBuf& data,
                                    int            keysize,
                                    TxtChunkType   type)
    {
        DataBuf arr;

        if(type == zTXt_Chunk)
        {
            enforce(data.size_ >= Safe::add(keysize, 2), Exiv2::kerCorruptedMetadata);

            // Extract a deflate compressed Latin-1 text chunk

            // we get the compression method after the key
            const byte* compressionMethod = data.pData_ + keysize + 1;
            if ( *compressionMethod != 0x00 )
            {
                // then it isn't zlib compressed and we are sunk
#ifdef DEBUG
                std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard zTXt compression method.\n";
#endif
                throw Error(kerFailedToReadImageData);
            }

            // compressed string after the compression technique spec
            const byte* compressedText      = data.pData_ + keysize + 2;
            long compressedTextSize = data.size_  - keysize - 2;
            enforce(compressedTextSize < data.size_, kerCorruptedMetadata);

            zlibUncompress(compressedText, compressedTextSize, arr);
        }
        else if(type == tEXt_Chunk)
        {
            enforce(data.size_ >= Safe::add(keysize, 1), Exiv2::kerCorruptedMetadata);
            // Extract a non-compressed Latin-1 text chunk

            // the text comes after the key, but isn't null terminated
            const byte* text = data.pData_ + keysize + 1;
            long textsize    = data.size_  - keysize - 1;

            arr = DataBuf(text, textsize);
        }
        else if(type == iTXt_Chunk)
        {
            enforce(data.size_ >= Safe::add(keysize, 3), Exiv2::kerCorruptedMetadata);
            const size_t nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0');
            enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata);

            // Extract a deflate compressed or uncompressed UTF-8 text chunk

            // we get the compression flag after the key
            const byte compressionFlag   = data.pData_[keysize + 1];
            // we get the compression method after the compression flag
            const byte compressionMethod = data.pData_[keysize + 2];

            enforce(compressionFlag == 0x00 || compressionFlag == 0x01, Exiv2::kerCorruptedMetadata);
            enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata);

            // language description string after the compression technique spec
            const size_t languageTextMaxSize = data.size_ - keysize - 3;
            std::string languageText =
                string_from_unterminated((const char*)(data.pData_ + Safe::add(keysize, 3)), languageTextMaxSize);
            const size_t languageTextSize = languageText.size();

            enforce(static_cast<unsigned long>(data.size_) >=
                    Safe::add(static_cast<size_t>(Safe::add(keysize, 4)), languageTextSize),
                    Exiv2::kerCorruptedMetadata);
            // translated keyword string after the language description
            std::string translatedKeyText =
                string_from_unterminated((const char*)(data.pData_ + keysize + 3 + languageTextSize + 1),
                                         data.size_ - (keysize + 3 + languageTextSize + 1));
            const unsigned int translatedKeyTextSize = static_cast<unsigned int>(translatedKeyText.size());

            if ((compressionFlag == 0x00) || (compressionFlag == 0x01 && compressionMethod == 0x00)) {
                enforce(Safe::add(static_cast<unsigned int>(keysize + 3 + languageTextSize + 1),
                                  Safe::add(translatedKeyTextSize, 1u)) <= static_cast<unsigned int>(data.size_),
                        Exiv2::kerCorruptedMetadata);

                const byte* text = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1;
                const long textsize = static_cast<long>(data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1));

                if (compressionFlag == 0x00) {
                    // then it's an uncompressed iTXt chunk
#ifdef DEBUG
                    std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n";
#endif

                    arr.alloc(textsize);
                    arr = DataBuf(text, textsize);
                } else if (compressionFlag == 0x01 && compressionMethod == 0x00) {
                    // then it's a zlib compressed iTXt chunk
#ifdef DEBUG
                    std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n";
#endif

                    // the compressed text comes after the translated keyword, but isn't null terminated
                    zlibUncompress(text, textsize, arr);
                }
            } else {
                // then it isn't zlib compressed and we are sunk
#ifdef DEBUG
                std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n";
#endif
                throw Error(kerFailedToReadImageData);
            }
        }
        else
        {
#ifdef DEBUG
            std::cerr << "Exiv2::PngChunk::parseTXTChunk: We found a field, not expected though\n";
#endif
            throw Error(kerFailedToReadImageData);
        }

        return arr;

    } // PngChunk::parsePngChunk