Blame SOURCES/exiv2-CVE-2018-10958.patch

3f58c5
diff --git a/include/exiv2/error.hpp b/include/exiv2/error.hpp
3f58c5
index 24a70bf6..cc67725b 100644
3f58c5
--- a/include/exiv2/error.hpp
3f58c5
+++ b/include/exiv2/error.hpp
3f58c5
@@ -192,6 +192,74 @@ namespace Exiv2 {
3f58c5
         return os << error.what();
3f58c5
     }
3f58c5
3f58c5
+    //! Complete list of all Exiv2 error codes
3f58c5
+    enum ErrorCode {
3f58c5
+        kerGeneralError = -1,
3f58c5
+        kerSuccess = 0,
3f58c5
+        kerErrorMessage,
3f58c5
+        kerCallFailed,
3f58c5
+        kerNotAnImage,
3f58c5
+        kerInvalidDataset,
3f58c5
+        kerInvalidRecord,
3f58c5
+        kerInvalidKey,
3f58c5
+        kerInvalidTag,
3f58c5
+        kerValueNotSet,
3f58c5
+        kerDataSourceOpenFailed,
3f58c5
+        kerFileOpenFailed,
3f58c5
+        kerFileContainsUnknownImageType,
3f58c5
+        kerMemoryContainsUnknownImageType,
3f58c5
+        kerUnsupportedImageType,
3f58c5
+        kerFailedToReadImageData,
3f58c5
+        kerNotAJpeg,
3f58c5
+        kerFailedToMapFileForReadWrite,
3f58c5
+        kerFileRenameFailed,
3f58c5
+        kerTransferFailed,
3f58c5
+        kerMemoryTransferFailed,
3f58c5
+        kerInputDataReadFailed,
3f58c5
+        kerImageWriteFailed,
3f58c5
+        kerNoImageInInputData,
3f58c5
+        kerInvalidIfdId,
3f58c5
+        //! Entry::setValue: Value too large
3f58c5
+        kerValueTooLarge,
3f58c5
+        //! Entry::setDataArea: Value too large
3f58c5
+        kerDataAreaValueTooLarge,
3f58c5
+        kerOffsetOutOfRange,
3f58c5
+        kerUnsupportedDataAreaOffsetType,
3f58c5
+        kerInvalidCharset,
3f58c5
+        kerUnsupportedDateFormat,
3f58c5
+        kerUnsupportedTimeFormat,
3f58c5
+        kerWritingImageFormatUnsupported,
3f58c5
+        kerInvalidSettingForImage,
3f58c5
+        kerNotACrwImage,
3f58c5
+        kerFunctionNotSupported,
3f58c5
+        kerNoNamespaceInfoForXmpPrefix,
3f58c5
+        kerNoPrefixForNamespace,
3f58c5
+        kerTooLargeJpegSegment,
3f58c5
+        kerUnhandledXmpdatum,
3f58c5
+        kerUnhandledXmpNode,
3f58c5
+        kerXMPToolkitError,
3f58c5
+        kerDecodeLangAltPropertyFailed,
3f58c5
+        kerDecodeLangAltQualifierFailed,
3f58c5
+        kerEncodeLangAltPropertyFailed,
3f58c5
+        kerPropertyNameIdentificationFailed,
3f58c5
+        kerSchemaNamespaceNotRegistered,
3f58c5
+        kerNoNamespaceForPrefix,
3f58c5
+        kerAliasesNotSupported,
3f58c5
+        kerInvalidXmpText,
3f58c5
+        kerTooManyTiffDirectoryEntries,
3f58c5
+        kerMultipleTiffArrayElementTagsInDirectory,
3f58c5
+        kerWrongTiffArrayElementTagType,
3f58c5
+        kerInvalidKeyXmpValue,
3f58c5
+        kerInvalidIccProfile,
3f58c5
+        kerInvalidXMP,
3f58c5
+        kerTiffDirectoryTooLarge,
3f58c5
+        kerInvalidTypeValue,
3f58c5
+        kerInvalidMalloc,
3f58c5
+        kerCorruptedMetadata,
3f58c5
+        kerArithmeticOverflow,
3f58c5
+        kerMallocFailed,
3f58c5
+    };
3f58c5
+
3f58c5
     /*!
3f58c5
       @brief Simple error class used for exceptions. An output operator is
3f58c5
              provided to print errors to a stream.
3f58c5
3f58c5
diff --git a/src/enforce.hpp b/src/enforce.hpp
3f58c5
new file mode 100644
3f58c5
index 00000000..b2d77eea
3f58c5
--- /dev/null
3f58c5
+++ b/src/enforce.hpp
3f58c5
@@ -0,0 +1,96 @@
3f58c5
+// ********************************************************* -*- C++ -*-
3f58c5
+/*
3f58c5
+ * Copyright (C) 2004-2018 Exiv2 maintainers
3f58c5
+ *
3f58c5
+ * This program is part of the Exiv2 distribution.
3f58c5
+ *
3f58c5
+ * This program is free software; you can redistribute it and/or
3f58c5
+ * modify it under the terms of the GNU General Public License
3f58c5
+ * as published by the Free Software Foundation; either version 2
3f58c5
+ * of the License, or (at your option) any later version.
3f58c5
+ *
3f58c5
+ * This program is distributed in the hope that it will be useful,
3f58c5
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3f58c5
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3f58c5
+ * GNU General Public License for more details.
3f58c5
+ *
3f58c5
+ * You should have received a copy of the GNU General Public License
3f58c5
+ * along with this program; if not, write to the Free Software
3f58c5
+ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
3f58c5
+ */
3f58c5
+/*!
3f58c5
+  @file    enforce.hpp
3f58c5
+  @brief   Port of D's enforce() to C++ & Exiv2
3f58c5
+  @author  Dan Čermák (D4N)
3f58c5
+           dan.cermak@cgc-instruments.com
3f58c5
+  @date    11-March-18, D4N: created
3f58c5
+ */
3f58c5
+
3f58c5
+#include <string>
3f58c5
+
3f58c5
+#include "error.hpp"
3f58c5
+
3f58c5
+/*!
3f58c5
+ * @brief Ensure that condition is true, otherwise throw an exception of the
3f58c5
+ * type exception_t
3f58c5
+ *
3f58c5
+ * @tparam exception_t  Exception type that is thrown, must provide a
3f58c5
+ * constructor that accepts a single argument to which arg1 is forwarded.
3f58c5
+ *
3f58c5
+ * @todo once we have C++>=11 use variadic templates and std::forward to remove
3f58c5
+ * all overloads of enforce
3f58c5
+ */
3f58c5
+template <typename exception_t, typename T>
3f58c5
+inline void enforce(bool condition, const T& arg1)
3f58c5
+{
3f58c5
+    if (!condition) {
3f58c5
+        throw exception_t(arg1);
3f58c5
+    }
3f58c5
+}
3f58c5
+
3f58c5
+/*!
3f58c5
+ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with
3f58c5
+ * the given error_code.
3f58c5
+ */
3f58c5
+inline void enforce(bool condition, Exiv2::ErrorCode err_code)
3f58c5
+{
3f58c5
+    if (!condition) {
3f58c5
+        throw Exiv2::Error(err_code);
3f58c5
+    }
3f58c5
+}
3f58c5
+
3f58c5
+/*!
3f58c5
+ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with
3f58c5
+ * the given error_code & arg1.
3f58c5
+ */
3f58c5
+template <typename T>
3f58c5
+inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1)
3f58c5
+{
3f58c5
+    if (!condition) {
3f58c5
+        throw Exiv2::Error(err_code, arg1);
3f58c5
+    }
3f58c5
+}
3f58c5
+
3f58c5
+/*!
3f58c5
+ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with
3f58c5
+ * the given error_code, arg1 & arg2.
3f58c5
+ */
3f58c5
+template <typename T, typename U>
3f58c5
+inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2)
3f58c5
+{
3f58c5
+    if (!condition) {
3f58c5
+        throw Exiv2::Error(err_code, arg1, arg2);
3f58c5
+    }
3f58c5
+}
3f58c5
+
3f58c5
+/*!
3f58c5
+ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with
3f58c5
+ * the given error_code, arg1, arg2 & arg3.
3f58c5
+ */
3f58c5
+template <typename T, typename U, typename V>
3f58c5
+inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2, const V& arg3)
3f58c5
+{
3f58c5
+    if (!condition) {
3f58c5
+        throw Exiv2::Error(err_code, arg1, arg2, arg3);
3f58c5
+    }
3f58c5
+}
3f58c5
3f58c5
diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp
3f58c5
index 4dcca4d..aae0f5f 100644
3f58c5
--- a/src/pngchunk.cpp
3f58c5
+++ b/src/pngchunk.cpp
3f58c5
@@ -37,6 +37,7 @@ EXIV2_RCSID("@(#) $Id$")
3f58c5
 #include "iptc.hpp"
3f58c5
 #include "image.hpp"
3f58c5
 #include "error.hpp"
3f58c5
+#include "enforce.hpp"
3f58c5
3f58c5
 // + standard includes
3f58c5
 #include <sstream>
3f58c5
@@ -46,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$")
3f58c5
 #include <iostream>
3f58c5
 #include <cassert>
3f58c5
 #include <cstdio>
3f58c5
+#include <algorithm>
3f58c5
3f58c5
 #include <zlib.h>     // To uncompress or compress text chunk
3f58c5
3f58c5
@@ -86,7 +88,7 @@ namespace Exiv2 {
3f58c5
3f58c5
 #ifdef DEBUG
3f58c5
         std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk data: "
3f58c5
-                  << std::string((const char*)arr.pData_, arr.size_) << "\n";
3f58c5
+                  << std::string((const char*)arr.pData_, arr.size_) << std::endl;
3f58c5
 #endif
3f58c5
         parseChunkContent(pImage, key.pData_, key.size_, arr);
3f58c5
3f58c5
@@ -99,7 +101,7 @@ namespace Exiv2 {
3f58c5
3f58c5
 #ifdef DEBUG
3f58c5
         std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk key: "
3f58c5
-                  << std::string((const char*)key.pData_, key.size_) << "\n";
3f58c5
+                  << std::string((const char*)key.pData_, key.size_) << std::endl;
3f58c5
 #endif
3f58c5
         return parseTXTChunk(data, key.size_, type);
3f58c5
3f58c5
@@ -164,12 +166,18 @@ namespace Exiv2 {
3f58c5
         }
3f58c5
         else if(type == iTXt_Chunk)
3f58c5
         {
3f58c5
+            const int nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0');
3f58c5
+
3f58c5
+            enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata);
3f58c5
+
3f58c5
             // Extract a deflate compressed or uncompressed UTF-8 text chunk
3f58c5
3f58c5
             // we get the compression flag after the key
3f58c5
-            const byte* compressionFlag   = data.pData_ + keysize + 1;
3f58c5
+            const byte compressionFlag   = data.pData_[keysize + 1];
3f58c5
             // we get the compression method after the compression flag
3f58c5
-            const byte* compressionMethod = data.pData_ + keysize + 2;
3f58c5
+            const byte compressionMethod = data.pData_[keysize + 2];
3f58c5
+            enforce(compressionFlag == 0x00 || compressionFlag == 0x01, Exiv2::kerCorruptedMetadata);
3f58c5
+            enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata);
3f58c5
             // language description string after the compression technique spec
3f58c5
             std::string languageText((const char*)(data.pData_ + keysize + 3));
3f58c5
             unsigned int languageTextSize = static_cast<unsigned int>(languageText.size());
3f58c5
@@ -177,7 +185,7 @@ namespace Exiv2 {
3f58c5
             std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1));
3f58c5
             unsigned int translatedKeyTextSize = static_cast<unsigned int>(translatedKeyText.size());
3f58c5
3f58c5
-            if ( compressionFlag[0] == 0x00 )
3f58c5
+            if ( compressionFlag == 0x00 )
3f58c5
             {
3f58c5
                 // then it's an uncompressed iTXt chunk
3f58c5
 #ifdef DEBUG
3f58c5
@@ -191,7 +199,7 @@ namespace Exiv2 {
3f58c5
                 arr.alloc(textsize);
3f58c5
                 arr = DataBuf(text, textsize);
3f58c5
             }
3f58c5
-            else if ( compressionFlag[0] == 0x01 && compressionMethod[0] == 0x00 )
3f58c5
+            else if ( compressionFlag == 0x01 && compressionMethod == 0x00 )
3f58c5
             {
3f58c5
                 // then it's a zlib compressed iTXt chunk
3f58c5
 #ifdef DEBUG
3f58c5
diff --git a/src/pngimage.cpp b/src/pngimage.cpp
3f58c5
index ed7399a..991da6c 100644
3f58c5
--- a/src/pngimage.cpp
3f58c5
+++ b/src/pngimage.cpp
3f58c5
@@ -375,7 +375,7 @@ namespace Exiv2 {
3f58c5
     void PngImage::readMetadata()
3f58c5
     {
3f58c5
 #ifdef DEBUG
3f58c5
-        std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << "\n";
3f58c5
+        std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << std::endl;
3f58c5
 #endif
3f58c5
         if (io_->open() != 0)
3f58c5
         {
3f58c5
@@ -398,7 +398,7 @@ namespace Exiv2 {
3f58c5
             // Read chunk header.
3f58c5
3f58c5
 #ifdef DEBUG
3f58c5
-            std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << "\n";
3f58c5
+            std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << std::endl;
3f58c5
 #endif
3f58c5
             std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_);
3f58c5
             long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_);
3f58c5
@@ -432,14 +432,14 @@ namespace Exiv2 {
3f58c5
                 {
3f58c5
                     // Last chunk found: we stop parsing.
3f58c5
 #ifdef DEBUG
3f58c5
-                    std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk (length: " << dataOffset << ")\n";
3f58c5
+                    std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk with length: " << dataOffset << std::endl;
3f58c5
 #endif
3f58c5
                     return;
3f58c5
                 }
3f58c5
                 else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4))
3f58c5
                 {
3f58c5
 #ifdef DEBUG
3f58c5
-                    std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk (length: " << dataOffset << ")\n";
3f58c5
+                    std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk with length: " << dataOffset << std::endl;
3f58c5
 #endif
3f58c5
                     if (cdataBuf.size_ >= 8) {
3f58c5
                         PngChunk::decodeIHDRChunk(cdataBuf, &pixelWidth_, &pixelHeight_);
3f58c5
@@ -448,21 +448,21 @@ namespace Exiv2 {
3f58c5
                 else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4))
3f58c5
                 {
3f58c5
 #ifdef DEBUG
3f58c5
-                    std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk (length: " << dataOffset << ")\n";
3f58c5
+                    std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk with length: " << dataOffset << std::endl;
3f58c5
 #endif
3f58c5
                     PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::tEXt_Chunk);
3f58c5
                 }
3f58c5
                 else if (!memcmp(cheaderBuf.pData_ + 4, "zTXt", 4))
3f58c5
                 {
3f58c5
 #ifdef DEBUG
3f58c5
-                    std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk (length: " << dataOffset << ")\n";
3f58c5
+                    std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk with length: " << dataOffset << std::endl;
3f58c5
 #endif
3f58c5
                     PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::zTXt_Chunk);
3f58c5
                 }
3f58c5
                 else if (!memcmp(cheaderBuf.pData_ + 4, "iTXt", 4))
3f58c5
                 {
3f58c5
 #ifdef DEBUG
3f58c5
-                    std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk (length: " << dataOffset << ")\n";
3f58c5
+                    std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk with length: " << dataOffset << std::endl;
3f58c5
 #endif
3f58c5
                     PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::iTXt_Chunk);
3f58c5
                 }
3f58c5
@@ -481,7 +481,7 @@ namespace Exiv2 {
3f58c5
3f58c5
             // Move to the next chunk: chunk data size + 4 CRC bytes.
3f58c5
 #ifdef DEBUG
3f58c5
-            std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << "\n";
3f58c5
+            std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << std::endl;
3f58c5
 #endif
3f58c5
             io_->seek(dataOffset + 4 , BasicIo::cur);
3f58c5
             if (io_->error() || io_->eof()) throw Error(14);
3f58c5
@@ -511,8 +511,8 @@ namespace Exiv2 {
3f58c5
         if (!outIo.isopen()) throw Error(21);
3f58c5
3f58c5
 #ifdef DEBUG
3f58c5
-        std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n";
3f58c5
-        std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n";
3f58c5
+        std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << std::endl;
3f58c5
+        std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << std::endl;
3f58c5
 #endif
3f58c5
3f58c5
         // Ensure that this is the correct image type