commit 66fd93ad955e0d6f60967d9a8e6cdb76a1b9fdc1 Author: overweight <5324761+overweight@user.noreply.gitee.com> Date: Mon Sep 30 10:38:30 2019 -0400 Package init diff --git a/0006-1296-Fix-submitted.patch b/0006-1296-Fix-submitted.patch new file mode 100644 index 0000000..bc3c413 --- /dev/null +++ b/0006-1296-Fix-submitted.patch @@ -0,0 +1,25 @@ +From 2f8681e120d277e418941c4361c83b5028f67fd8 Mon Sep 17 00:00:00 2001 +From: clanmills +Date: Sat, 27 May 2017 10:18:17 +0100 +Subject: [PATCH 6/6] #1296 Fix submitted. + +--- + src/tiffcomposite.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/tiffcomposite.cpp b/src/tiffcomposite.cpp +index c6b860d..0c9b9c4 100644 +--- a/src/tiffcomposite.cpp ++++ b/src/tiffcomposite.cpp +@@ -1611,6 +1611,8 @@ namespace Exiv2 { + uint32_t TiffImageEntry::doWriteImage(IoWrapper& ioWrapper, + ByteOrder /*byteOrder*/) const + { ++ if ( !pValue() ) throw Error(21); // #1296 ++ + uint32_t len = pValue()->sizeDataArea(); + if (len > 0) { + #ifdef DEBUG +-- +2.9.4 + diff --git a/CVE-2017-1000126.patch b/CVE-2017-1000126.patch new file mode 100644 index 0000000..a73aee5 --- /dev/null +++ b/CVE-2017-1000126.patch @@ -0,0 +1,42 @@ +From 0d04a6d900010c754d578ef27d5c26190dc59d2b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Mon, 26 Mar 2018 00:34:25 +0200 +Subject: [PATCH] Fix CVE-2017-1000126 + +CVE-2017-1000126 is a Stack out of bounds read in the WebP parser caused by the +parameter size & filesize being too large, causing the parser to land in an +infinite loop and eventually crash. Enforcing that the size over which the +parser iterates is smaller than the file fixes this issue. + +This fixes #175. + +(cherry picked from commit 3c20cc06a9ede4e277a9efe94e211c20ceb0ce8d) +--- + src/webpimage.cpp | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/webpimage.cpp b/src/webpimage.cpp +index 45d642109..eb5649728 100644 +--- a/src/webpimage.cpp ++++ b/src/webpimage.cpp +@@ -493,7 +494,9 @@ namespace Exiv2 { + + io_->read(data, WEBP_TAG_SIZE * 3); + +- WebPImage::decodeChunks(Exiv2::getULong(data + WEBP_TAG_SIZE, littleEndian) + 12); ++ const uint32_t filesize = Exiv2::getULong(data + WEBP_TAG_SIZE, littleEndian) + 8; ++ enforce(filesize <= io_->size(), Exiv2::kerCorruptedMetadata); ++ WebPImage::decodeChunks(filesize); + + } // WebPImage::readMetadata + +@@ -511,7 +514,8 @@ namespace Exiv2 { + while ( !io_->eof() && (uint64_t) io_->tell() < filesize) { + io_->read(chunkId.pData_, WEBP_TAG_SIZE); + io_->read(size_buff, WEBP_TAG_SIZE); +- long size = Exiv2::getULong(size_buff, littleEndian); ++ const uint32_t size = Exiv2::getULong(size_buff, littleEndian); ++ enforce(size <= (filesize - io_->tell()), Exiv2::kerCorruptedMetadata); + + DataBuf payload(size); + diff --git a/CVE-2018-17229-CVE-2018-17230.patch b/CVE-2018-17229-CVE-2018-17230.patch new file mode 100644 index 0000000..eeeaffd --- /dev/null +++ b/CVE-2018-17229-CVE-2018-17230.patch @@ -0,0 +1,22 @@ +From afb98cbc6e288dc8ea75f3394a347fb9b37abc55 Mon Sep 17 00:00:00 2001 +From: Robin Mills +Date: Mon, 22 Jan 2018 23:27:08 +0100 +Subject: [PATCH] Allocate correct amount of memory for the ICC profile + +--- + src/tiffimage.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp +index 8731449cd..a69c7afd5 100644 +--- a/src/tiffimage.cpp ++++ b/src/tiffimage.cpp +@@ -191,7 +191,7 @@ namespace Exiv2 { + Exiv2::ExifKey key("Exif.Image.InterColorProfile"); + Exiv2::ExifData::iterator pos = exifData_.findKey(key); + if ( pos != exifData_.end() ) { +- iccProfile_.alloc(pos->count()); ++ iccProfile_.alloc(pos->count()*pos->typeSize()); + pos->copy(iccProfile_.pData_,bo); + } + diff --git a/CVE-2018-17282.patch b/CVE-2018-17282.patch new file mode 100644 index 0000000..174f8c6 --- /dev/null +++ b/CVE-2018-17282.patch @@ -0,0 +1,40 @@ +--- a/src/tiffimage.cpp ++++ b/src/tiffimage.cpp +@@ -171,11 +171,15 @@ + #ifdef DEBUG + std::cerr << "Reading TIFF file " << io_->path() << "\n"; + #endif +- if (io_->open() != 0) throw Error(9, io_->path(), strError()); ++ if (io_->open() != 0) { ++ throw Error(kerDataSourceOpenFailed, io_->path(), strError()); ++ } ++ + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isTiffType(*io_, false)) { +- if (io_->error() || io_->eof()) throw Error(14); ++ if (io_->error() || io_->eof()) ++ throw Error(kerFailedToReadImageData); + throw Error(3, "TIFF"); + } + clearMetadata(); +@@ -190,12 +194,16 @@ + // read profile from the metadata + Exiv2::ExifKey key("Exif.Image.InterColorProfile"); + Exiv2::ExifData::iterator pos = exifData_.findKey(key); +- if ( pos != exifData_.end() ) { +- iccProfile_.alloc(pos->count()*pos->typeSize()); ++ if ( pos != exifData_.end() ) { ++ long size = pos->count() * pos->typeSize(); ++ if (size == 0) { ++ throw Error(kerFailedToReadImageData); ++ } ++ iccProfile_.alloc(size); + pos->copy(iccProfile_.pData_,bo); + } + +- } // TiffImage::readMetadata ++ } + + void TiffImage::writeMetadata() + { diff --git a/CVE-2018-18915.patch b/CVE-2018-18915.patch new file mode 100644 index 0000000..6de7cb8 --- /dev/null +++ b/CVE-2018-18915.patch @@ -0,0 +1,27 @@ +From ae49250942f4395639961abeed3c15920fcd7241 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luis=20D=C3=ADaz=20M=C3=A1s?= +Date: Sun, 4 Nov 2018 18:44:38 +0100 +Subject: [PATCH] Check in Image::printIFDStructure if seek and reads are OK + +--- + src/image.cpp | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/image.cpp b/src/image.cpp +index e0eebf3d4..75991f7d5 100644 +--- a/src/image.cpp ++++ b/src/image.cpp +@@ -344,8 +344,11 @@ namespace Exiv2 { + + do { + // Read top of directory +- io.seek(start,BasicIo::beg); +- io.read(dir.pData_, 2); ++ const int seekSuccess = !io.seek(start,BasicIo::beg); ++ const long bytesRead = io.read(dir.pData_, 2); ++ if (!seekSuccess || bytesRead == 0) { ++ throw Error(kerCorruptedMetadata); ++ } + uint16_t dirLength = byteSwap2(dir,0,bSwap); + + bool tooBig = dirLength > 500; diff --git a/CVE-2018-19107-CVE-2018-19108-1.patch b/CVE-2018-19107-CVE-2018-19108-1.patch new file mode 100644 index 0000000..9142d5c --- /dev/null +++ b/CVE-2018-19107-CVE-2018-19108-1.patch @@ -0,0 +1,31 @@ +From 68966932510213b5656fcf433ab6d7e26f48e23b Mon Sep 17 00:00:00 2001 +From: Luis Diaz Mas +Date: Sun, 4 Nov 2018 22:33:03 +0100 +Subject: [PATCH] PSD: Use Safe::add for preventing overflows in PSD files + +--- + src/psdimage.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/psdimage.cpp b/src/psdimage.cpp +index 02e0e87b0..a5a8d9fe9 100644 +--- a/src/psdimage.cpp ++++ b/src/psdimage.cpp +@@ -33,6 +33,7 @@ + #include "basicio.hpp" + #include "error.hpp" + #include "futils.hpp" ++#include "safe_op.hpp" + + // + standard includes + #include +@@ -228,7 +229,8 @@ namespace Exiv2 { + readResourceBlock(resourceId, resourceSize); + resourceSize = (resourceSize + 1) & ~1; // pad to even + io_->seek(curOffset + resourceSize, BasicIo::beg); +- resourcesLength -= (12 + resourceNameLength + resourceSize); ++ resourcesLength -= Safe::add(Safe::add(static_cast(12), resourceNameLength), ++ resourceSize); + } + + } // PsdImage::readMetadata diff --git a/CVE-2018-19107-CVE-2018-19108-2.patch b/CVE-2018-19107-CVE-2018-19108-2.patch new file mode 100644 index 0000000..755ca2d --- /dev/null +++ b/CVE-2018-19107-CVE-2018-19108-2.patch @@ -0,0 +1,32 @@ +From b7c71f3ad0386cd7af3b73443c0615ada073f0d5 Mon Sep 17 00:00:00 2001 +From: Luis Diaz Mas +Date: Mon, 5 Nov 2018 13:30:18 +0100 +Subject: [PATCH] PSD: enforce Length of image resource section < file size + +--- + src/psdimage.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/psdimage.cpp b/src/psdimage.cpp +index a5a8d9fe9..8ed67544d 100644 +--- a/src/psdimage.cpp ++++ b/src/psdimage.cpp +@@ -33,7 +33,9 @@ + #include "basicio.hpp" + #include "error.hpp" + #include "futils.hpp" ++ + #include "safe_op.hpp" ++#include "enforce.hpp" + + // + standard includes + #include +@@ -197,6 +199,8 @@ namespace Exiv2 { + throw Error(3, "Photoshop"); + } + uint32_t resourcesLength = getULong(buf, bigEndian); ++ enforce(resourcesLength < io_->size(), Exiv2::kerCorruptedMetadata); ++ + while (resourcesLength > 0) + { + if (io_->read(buf, 8) != 8) diff --git a/CVE-2018-19607.patch b/CVE-2018-19607.patch new file mode 100644 index 0000000..0f6841a --- /dev/null +++ b/CVE-2018-19607.patch @@ -0,0 +1,31 @@ +From 6e42c1b55e0fc4f360cc56010b0ffe19aa6062d9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luis=20D=C3=ADaz=20M=C3=A1s?= +Date: Mon, 26 Nov 2018 14:24:14 +0100 +Subject: [PATCH] Fix #561. Use proper counter for the idx variable + +--- + src/easyaccess.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/easyaccess.cpp b/src/easyaccess.cpp +index 1eed30c47..9302d6cc9 100644 +--- a/src/easyaccess.cpp ++++ b/src/easyaccess.cpp +@@ -154,7 +154,7 @@ namespace Exiv2 { + std::ostringstream os; + md_st->write(os, &ed); + bool ok = false; +- long st_val = parseLong(os.str(), ok); ++ const long st_val = parseLong(os.str(), ok); + // SensivityType out of range or cannot be parsed properly + if (!ok || st_val < 1 || st_val > 7) + break; +@@ -175,7 +175,7 @@ namespace Exiv2 { + md = md_st; + break; + } +- while (strcmp(sensKeys->keys[idx++], md_st->key().c_str()) != 0 && idx < cnt) {} ++ while (strcmp(sensKeys->keys[idx++], md_st->key().c_str()) != 0 && idx < sensKeys->count) {} + } + break; + } diff --git a/CVE-2019-13110.patch b/CVE-2019-13110.patch new file mode 100644 index 0000000..ea3e252 --- /dev/null +++ b/CVE-2019-13110.patch @@ -0,0 +1,11 @@ +--- a/src/crwimage.cpp 2019-07-17 16:22:44.284000000 +0800 ++++ b/src/crwimage_1.cpp 2019-07-17 16:23:57.032000000 +0800 +@@ -460,7 +460,7 @@ namespace Exiv2 { + #endif + o += 2; + for (uint16_t i = 0; i < count; ++i) { +- if (o + 10 > size) throw Error(33); ++ if (size < 10 || o > size-10) throw Error(33); + uint16_t tag = getUShort(pData + o, byteOrder); + CiffComponent::AutoPtr m; + switch (CiffComponent::typeId(tag)) { diff --git a/CVE-2019-13113.patch b/CVE-2019-13113.patch new file mode 100644 index 0000000..8b800de --- /dev/null +++ b/CVE-2019-13113.patch @@ -0,0 +1,38 @@ +Backported of: + +From 7798ae25574425271305fffe85de77bec8df03f1 Mon Sep 17 00:00:00 2001 +From: Kevin Backhouse +Date: Wed, 15 May 2019 10:12:02 +0100 +Subject: [PATCH] Throw an exception if the data location is invalid. (#842) +diff --git a/src/crwimage.cpp b/src/crwimage.cpp +index 9225ce5..5ccf3b1 100644 +--- a/src/crwimage.cpp ++++ b/src/crwimage.cpp +@@ -738,12 +738,11 @@ namespace Exiv2 { + + DataLocId CiffComponent::dataLocation(uint16_t tag) + { +- DataLocId di = invalidDataLocId; + switch (tag & 0xc000) { +- case 0x0000: di = valueData; break; +- case 0x4000: di = directoryData; break; ++ case 0x0000: return valueData; ++ case 0x4000: return directoryData; ++ default: throw Error(kerCorruptedMetadata); + } +- return di; + } // CiffComponent::dataLocation + + /*! +diff --git a/src/crwimage_int.hpp b/src/crwimage_int.hpp +index 5588ae5..1478c82 100644 +--- a/src/crwimage_int.hpp ++++ b/src/crwimage_int.hpp +@@ -78,7 +78,6 @@ namespace Exiv2 { + + //! Type to identify where the data is stored in a directory + enum DataLocId { +- invalidDataLocId, + valueData, + directoryData, + lastDataLocId diff --git a/CVE-2019-13114.patch b/CVE-2019-13114.patch new file mode 100644 index 0000000..3ee9433 --- /dev/null +++ b/CVE-2019-13114.patch @@ -0,0 +1,53 @@ +Backported of: + +From c1bee7319a8b9e0d38f1988d70dc4fa5c52b83d1 Mon Sep 17 00:00:00 2001 +From: Kevin Backhouse +Date: Tue, 30 Apr 2019 11:15:06 +0100 +Subject: [PATCH] Avoid null pointer exception due to NULL return value from + strchr. + +This fixes #793. +diff --git a/src/http.cpp b/src/http.cpp +index b8a429b..9c76f99 100644 +--- a/src/http.cpp ++++ b/src/http.cpp +@@ -339,10 +339,14 @@ int Exiv2::http(dict_t& request,dict_t& response,std::string& errors) + + // search for the body + for ( size_t b = 0 ; bSearching && b < lengthof(blankLines) ; b++ ) { +- if ( strstr(buffer,blankLines[b]) ) { ++ const char* blankLinePos = strstr(buffer,blankLines[b]); ++ if ( blankLinePos ) { + bSearching = false ; +- body = (int) ( strstr(buffer,blankLines[b]) - buffer ) + strlen(blankLines[b]) ; +- status = atoi(strchr(buffer,' ')) ; ++ body = blankLinePos - buffer + strlen(blankLines[b]); ++ const char* firstSpace = strchr(buffer,' '); ++ if (firstSpace) { ++ status = atoi(firstSpace); ++ } + } + } + +@@ -352,9 +356,19 @@ int Exiv2::http(dict_t& request,dict_t& response,std::string& errors) + char N = '\n'; + int i = 0 ; // initial byte in buffer + while(buffer[i] == N ) i++; +- h = strchr(h+i,N)+1; ++ h = strchr(h+i,N); ++ if (!h) { ++ status = 0; ++ break; ++ } ++ h++; + response[""]=std::string(buffer+i).substr(0,h-buffer-2); +- result = atoi(strchr(buffer,' ')); ++ const char* firstSpace = strchr(buffer,' '); ++ if ( !firstSpace ) { ++ status = 0; ++ break; ++ } ++ result = atoi(firstSpace); + char* c = strchr(h,C); + char* n = strchr(h,N); + while ( c && n && c < n && h < buffer+body ) { diff --git a/CVE-2019-14982.patch b/CVE-2019-14982.patch new file mode 100644 index 0000000..0ad7307 --- /dev/null +++ b/CVE-2019-14982.patch @@ -0,0 +1,29 @@ +From e925bc5addd881543fa503470c8a859e112cca62 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luis=20D=C3=ADaz=20M=C3=A1s?= +Date: Mon, 15 Jul 2019 20:04:39 +0200 +Subject: [PATCH] Fix integer overflow by checking size against header_size + +Note that the problem occurs when data_size is less than header_size +what causes a buffer overflow in &data[i] + +Co-Authored-By: D4N +--- + src/webpimage.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/webpimage.cpp b/src/webpimage.cpp +index 3e4773f0a..8bf98d33e 100644 +--- a/src/webpimage.cpp ++++ b/src/webpimage.cpp +@@ -827,8 +827,9 @@ namespace Exiv2 { + } + } + +- long WebPImage::getHeaderOffset(byte *data, long data_size, +- byte *header, long header_size) { ++ long WebPImage::getHeaderOffset(byte* data, long data_size, byte* header, long header_size) ++ { ++ if (data_size < header_size) { return -1; } + long pos = -1; + for (long i=0; i < data_size - header_size; i++) { + if (memcmp(header, &data[i], header_size) == 0) { diff --git a/exiv2-0.26.tar.gz b/exiv2-0.26.tar.gz new file mode 100644 index 0000000..addd655 Binary files /dev/null and b/exiv2-0.26.tar.gz differ diff --git a/exiv2-CVE-2017-11683.patch b/exiv2-CVE-2017-11683.patch new file mode 100644 index 0000000..aef92fc --- /dev/null +++ b/exiv2-CVE-2017-11683.patch @@ -0,0 +1,41 @@ +From 1f1715c086d8dcdf5165b19164af9aee7aa12e98 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Fri, 6 Oct 2017 00:37:43 +0200 +Subject: =?UTF-8?q?Use=20nullptr=20check=20instead=20of=20assertion,=20by?= + =?UTF-8?q?=20Rapha=C3=ABl=20Hertzog?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Source: +https://github.com/Exiv2/exiv2/issues/57#issuecomment-333086302 + +tc can be a null pointer when the TIFF tag is unknown (the factory +then returns an auto_ptr(0)) => as this can happen for corrupted +files, an explicit check should be used because an assertion can be +turned of in release mode (with NDEBUG defined) + +This also fixes #57 + +diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp +index 74f8d078..4ab733d4 100644 +--- a/src/tiffvisitor.cpp ++++ b/src/tiffvisitor.cpp +@@ -1294,11 +1294,12 @@ namespace Exiv2 { + } + uint16_t tag = getUShort(p, byteOrder()); + TiffComponent::AutoPtr tc = TiffCreator::create(tag, object->group()); +- // The assertion typically fails if a component is not configured in +- // the TIFF structure table +- assert(tc.get()); +- tc->setStart(p); +- object->addChild(tc); ++ if (tc.get()) { ++ tc->setStart(p); ++ object->addChild(tc); ++ } else { ++ EXV_WARNING << "Unable to handle tag " << tag << ".\n"; ++ } + p += 12; + } + diff --git a/exiv2-CVE-2017-14860.patch b/exiv2-CVE-2017-14860.patch new file mode 100644 index 0000000..73658b3 --- /dev/null +++ b/exiv2-CVE-2017-14860.patch @@ -0,0 +1,36 @@ +From 6ede8aa1975177705450abb816163f0b8d33a597 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Fri, 6 Oct 2017 23:09:08 +0200 +Subject: Fix for CVE-2017-14860 + +A heap buffer overflow could occur in memcpy when icc.size_ is larger +than data.size_ - pad, as then memcpy would read out of bounds of data. + +This commit adds a sanity check to iccLength (= icc.size_): if it is +larger than data.size_ - pad (i.e. an overflow would be caused) an +exception is thrown. + +This fixes #71. + +diff --git a/src/jp2image.cpp b/src/jp2image.cpp +index 1892fd43..09d023e2 100644 +--- a/src/jp2image.cpp ++++ b/src/jp2image.cpp +@@ -269,10 +269,15 @@ namespace Exiv2 + std::cout << "Exiv2::Jp2Image::readMetadata: " + << "Color data found" << std::endl; + #endif +- long pad = 3 ; // 3 padding bytes 2 0 0 ++ const long pad = 3 ; // 3 padding bytes 2 0 0 + DataBuf data(subBox.length+8); + io_->read(data.pData_,data.size_); +- long iccLength = getULong(data.pData_+pad, bigEndian); ++ const long iccLength = getULong(data.pData_+pad, bigEndian); ++ // subtracting pad from data.size_ is safe: ++ // size_ is at least 8 and pad = 3 ++ if (iccLength > data.size_ - pad) { ++ throw Error(58); ++ } + DataBuf icc(iccLength); + ::memcpy(icc.pData_,data.pData_+pad,icc.size_); + #ifdef DEBUG diff --git a/exiv2-CVE-2017-14864-CVE-2017-14862-CVE-2017-14859.patch b/exiv2-CVE-2017-14864-CVE-2017-14862-CVE-2017-14859.patch new file mode 100644 index 0000000..f3855d5 --- /dev/null +++ b/exiv2-CVE-2017-14864-CVE-2017-14862-CVE-2017-14859.patch @@ -0,0 +1,53 @@ +From d4e4288d839d0d9546a05986771f8738c382060c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Sat, 7 Oct 2017 23:08:36 +0200 +Subject: Fix for CVE-2017-14864, CVE-2017-14862 and CVE-2017-14859 + +The invalid memory dereference in +Exiv2::getULong()/Exiv2::StringValueBase::read()/Exiv2::DataValue::read() +is caused further up the call-stack, by +v->read(pData, size, byteOrder) in TiffReader::readTiffEntry() +passing an invalid pData pointer (pData points outside of the Tiff +file). pData can be set out of bounds in the (size > 4) branch where +baseOffset() and offset are added to pData_ without checking whether +the result is still in the file. As offset comes from an untrusted +source, an attacker can craft an arbitrarily large offset into the +file. + +This commit adds a check into the problematic branch, whether the +result of the addition would be out of bounds of the Tiff +file. Furthermore the whole operation is checked for possible +overflows. + +diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp +index 4ab733d4..ef13542e 100644 +--- a/src/tiffvisitor.cpp ++++ b/src/tiffvisitor.cpp +@@ -47,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$") + #include + #include + #include ++#include + + // ***************************************************************************** + namespace { +@@ -1517,7 +1518,19 @@ namespace Exiv2 { + size = 0; + } + if (size > 4) { ++ // setting pData to pData_ + baseOffset() + offset can result in pData pointing to invalid memory, ++ // as offset can be arbitrarily large ++ if ((static_cast(baseOffset()) > std::numeric_limits::max() - static_cast(offset)) ++ || (static_cast(baseOffset() + offset) > std::numeric_limits::max() - reinterpret_cast(pData_))) ++ { ++ throw Error(59); ++ } ++ if (pData_ + static_cast(baseOffset()) + static_cast(offset) > pLast_) { ++ throw Error(58); ++ } + pData = const_cast(pData_) + baseOffset() + offset; ++ ++ // check for size being invalid + if (size > static_cast(pLast_ - pData)) { + #ifndef SUPPRESS_WARNINGS + EXV_ERROR << "Upper boundary of data for " diff --git a/exiv2-CVE-2017-17669.patch b/exiv2-CVE-2017-17669.patch new file mode 100644 index 0000000..70eee29 --- /dev/null +++ b/exiv2-CVE-2017-17669.patch @@ -0,0 +1,37 @@ +From 06aa7ab69d0c4f3d14644bd84fc9d1346154430d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Mon, 22 Jan 2018 23:56:08 +0100 +Subject: Fix out of bounds read in src/pngchunk_int.cpp by @brianmay + +- consider that key is advanced by 8 bytes if stripHeader is true + => length is reduced by same amount + Fixed by adding offset to the check in the loop +- Rewrote loop so that keysize is checked before the next + iteration (preventing an out of bounds read) + +diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp +index da4ccd01..b54bcdac 100644 +--- a/src/pngchunk.cpp ++++ b/src/pngchunk.cpp +@@ -107,15 +107,17 @@ namespace Exiv2 { + { + // From a tEXt, zTXt, or iTXt chunk, + // we get the key, it's a null terminated string at the chunk start +- if (data.size_ <= (stripHeader ? 8 : 0)) throw Error(14); +- const byte *key = data.pData_ + (stripHeader ? 8 : 0); ++ const int offset = stripHeader ? 8 : 0; ++ if (data.size_ <= offset) throw Error(14); ++ const byte *key = data.pData_ + offset; + + // Find null string at end of key. + int keysize=0; +- for ( ; key[keysize] != 0 ; keysize++) ++ while (key[keysize] != 0) + { ++ keysize++; + // look if keysize is valid. +- if (keysize >= data.size_) ++ if (keysize+offset >= data.size_) + throw Error(14); + } + diff --git a/exiv2-CVE-2017-17723.patch b/exiv2-CVE-2017-17723.patch new file mode 100644 index 0000000..a71bf90 --- /dev/null +++ b/exiv2-CVE-2017-17723.patch @@ -0,0 +1,107 @@ +diff --git a/include/exiv2/value.hpp b/include/exiv2/value.hpp +index 64a8ca7..4e9f285 100644 +--- a/include/exiv2/value.hpp ++++ b/include/exiv2/value.hpp +@@ -1658,11 +1658,13 @@ namespace Exiv2 { + ok_ = true; + return static_cast(value_[n]); + } ++// #55 crash when value_[n].first == LONG_MIN ++#define LARGE_INT 1000000 + // Specialization for rational + template<> + inline long ValueType::toLong(long n) const + { +- ok_ = (value_[n].second != 0); ++ ok_ = (value_[n].second != 0 && -LARGE_INT < value_[n].first && value_[n].first < LARGE_INT); + if (!ok_) return 0; + return value_[n].first / value_[n].second; + } +@@ -1670,7 +1672,7 @@ namespace Exiv2 { + template<> + inline long ValueType::toLong(long n) const + { +- ok_ = (value_[n].second != 0); ++ ok_ = (value_[n].second != 0 && value_[n].first < LARGE_INT); + if (!ok_) return 0; + return value_[n].first / value_[n].second; + } +diff --git a/src/basicio.cpp b/src/basicio.cpp +index 95589cd..f2e1518 100644 +--- a/src/basicio.cpp ++++ b/src/basicio.cpp +@@ -990,6 +990,7 @@ namespace Exiv2 { + DataBuf FileIo::read(long rcount) + { + assert(p_->fp_ != 0); ++ if ( (size_t) rcount > size() ) throw Error(57); + DataBuf buf(rcount); + long readCount = read(buf.pData_, buf.size_); + buf.size_ = readCount; +diff --git a/src/error.cpp b/src/error.cpp +index 80378c1..e90a9c0 100644 +--- a/src/error.cpp ++++ b/src/error.cpp +@@ -106,6 +106,9 @@ namespace { + { 52, N_("%1 has invalid XMP value type `%2'") }, // %1=key, %2=value type + { 53, N_("Not a valid ICC Profile") }, + { 54, N_("Not valid XMP") }, ++ { 55, N_("tiff directory length is too large") }, ++ { 56, N_("invalid type value detected in Image::printIFDStructure") }, ++ { 57, N_("invalid memory allocation request") }, + }; + + } +diff --git a/src/image.cpp b/src/image.cpp +index 0d82804..ec5b873 100644 +--- a/src/image.cpp ++++ b/src/image.cpp +@@ -399,7 +399,13 @@ namespace Exiv2 { + ; + + // if ( offset > io.size() ) offset = 0; // Denial of service? +- DataBuf buf(size*count + pad+20); // allocate a buffer ++ ++ // #55 memory allocation crash test/data/POC8 ++ long long allocate = (long long) (size*count + pad+20); ++ if ( allocate > (long long) io.size() ) { ++ throw Error(57); ++ } ++ DataBuf buf(allocate); // allocate a buffer + std::memcpy(buf.pData_,dir.pData_+8,4); // copy dir[8:11] into buffer (short strings) + if ( count*size > 4 ) { // read into buffer + size_t restore = io.tell(); // save +diff --git a/test/bugfixes-test.sh b/test/bugfixes-test.sh +index f91c675..c90ae55 100755 +--- a/test/bugfixes-test.sh ++++ b/test/bugfixes-test.sh +@@ -602,6 +602,7 @@ source ./functions.source + runTest exiv2 -pX $filename | xmllint --format - + + num=1231 ++ printf "$num " >&3 + for X in a b; do + filename=exiv2-bug$num$X.jpg + echo '------>' Bug $filename '<-------' >&2 +@@ -622,6 +623,7 @@ source ./functions.source + runTest exiv2 -pa $filename + + num=1252 ++ printf "$num " >&3 + for X in a b; do + filename=exiv2-bug$num$X.exv + echo '------>' Bug $filename '<-------' >&2 +@@ -629,6 +631,13 @@ source ./functions.source + runTest exiv2 -pa --grep lens/i $filename + done + ++ num=g55 ++ printf "$num " >&3 ++ filename=POC8 ++ echo '------>' Bug $filename '<-------' >&2 ++ copyTestFile $filename ++ runTest exiv2 $filename 2>/dev/null ++ + ) 3>&1 > $results 2>&1 + + printf "\n" diff --git a/exiv2-CVE-2017-17725.patch b/exiv2-CVE-2017-17725.patch new file mode 100644 index 0000000..a7eef96 --- /dev/null +++ b/exiv2-CVE-2017-17725.patch @@ -0,0 +1,351 @@ +From 7c6f59619616a01e242401cf4c8e06428539a035 Mon Sep 17 00:00:00 2001 +From: Luis Diaz Mas +Date: Sat, 16 Dec 2017 16:05:08 +0100 +Subject: Fix arithmetic operation overflow + + +diff --git a/src/jp2image.cpp b/src/jp2image.cpp +index 09d023e2..a308bfd9 100644 +--- a/src/jp2image.cpp ++++ b/src/jp2image.cpp +@@ -41,6 +41,7 @@ EXIV2_RCSID("@(#) $Id$") + #include "error.hpp" + #include "futils.hpp" + #include "types.hpp" ++#include "safe_op.hpp" + + // + standard includes + #include +@@ -269,15 +270,16 @@ namespace Exiv2 + std::cout << "Exiv2::Jp2Image::readMetadata: " + << "Color data found" << std::endl; + #endif ++ + const long pad = 3 ; // 3 padding bytes 2 0 0 +- DataBuf data(subBox.length+8); ++ DataBuf data(Safe::add(subBox.length, static_cast(8))); + io_->read(data.pData_,data.size_); + const long iccLength = getULong(data.pData_+pad, bigEndian); + // subtracting pad from data.size_ is safe: + // size_ is at least 8 and pad = 3 + if (iccLength > data.size_ - pad) { + throw Error(58); +- } ++ } + DataBuf icc(iccLength); + ::memcpy(icc.pData_,data.pData_+pad,icc.size_); + #ifdef DEBUG +diff --git a/src/safe_op.hpp b/src/safe_op.hpp +new file mode 100644 +index 00000000..55d690e3 +--- /dev/null ++++ b/src/safe_op.hpp +@@ -0,0 +1,308 @@ ++// ********************************************************* -*- C++ -*- ++/* ++ * Copyright (C) 2004-2017 Exiv2 maintainers ++ * ++ * This program is part of the Exiv2 distribution. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. ++ */ ++/*! ++ @file safe_op.hpp ++ @brief Overflow checks for integers ++ @author Dan Čermák (D4N) ++ dan.cermak@cgc-instruments.com ++ @date 14-Dec-17, D4N: created ++ */ ++ ++#ifndef SAFE_OP_HPP_ ++#define SAFE_OP_HPP_ ++ ++#include ++#include ++ ++#ifdef _MSC_VER ++#include ++#endif ++ ++/*! ++ * @brief Arithmetic operations with overflow checks ++ */ ++namespace Safe ++{ ++ /*! ++ * @brief Helper structs for providing integer overflow checks. ++ * ++ * This namespace contains the internal helper structs fallback_add_overflow ++ * and builtin_add_overflow. Both have a public static member function add ++ * with the following interface: ++ * ++ * bool add(T summand_1, T summand_2, T& result) ++ * ++ * where T is the type over which the struct is templated. ++ * ++ * The function performs a check whether the addition summand_1 + summand_2 ++ * can be performed without an overflow. If the operation would overflow, ++ * true is returned and the addition is not performed if it would result in ++ * undefined behavior. If no overflow occurs, the sum is saved in result and ++ * false is returned. ++ * ++ * fallback_add_overflow implements a portable but slower overflow check. ++ * builtin_add_overflow uses compiler builtins (when available) and should ++ * be considerably faster. As builtins are not available for all types, ++ * builtin_add_overflow falls back to fallback_add_overflow when no builtin ++ * is available. ++ */ ++ namespace Internal ++ { ++ /*! ++ * @brief Helper struct to determine whether a type is signed or unsigned ++ ++ * This struct is a backport of std::is_signed from C++11. It has a public ++ * enum with the property VALUE which is true when the type is signed or ++ * false if it is unsigned. ++ */ ++ template ++ struct is_signed ++ { ++ enum ++ { ++ VALUE = T(-1) < T(0) ++ }; ++ }; ++ ++ /*! ++ * @brief Helper struct for SFINAE, from C++11 ++ ++ * This struct has a public typedef called type typedef'd to T if B is ++ * true. Otherwise there is no typedef. ++ */ ++ template ++ struct enable_if ++ { ++ }; ++ ++ /*! ++ * @brief Specialization of enable_if for the case B == true ++ */ ++ template ++ struct enable_if ++ { ++ typedef T type; ++ }; ++ ++ /*! ++ * @brief Fallback overflow checker, specialized via SFINAE ++ * ++ * This struct implements a 'fallback' addition with an overflow check, ++ * i.e. it does not rely on compiler intrinsics. It is specialized via ++ * SFINAE for signed and unsigned integer types and provides a public ++ * static member function add. ++ */ ++ template ++ struct fallback_add_overflow; ++ ++ /*! ++ * @brief Overload of fallback_add_overflow for signed integers ++ */ ++ template ++ struct fallback_add_overflow::VALUE>::type> ++ { ++ /*! ++ * @brief Adds the two summands only if no overflow occurs ++ * ++ * This function performs a check if summand_1 + summand_2 would ++ * overflow and returns true in that case. If no overflow occurs, ++ * the sum is saved in result and false is returned. ++ * ++ * @return true on overflow, false on no overflow ++ * ++ * The check for an overflow is performed before the addition to ++ * ensure that no undefined behavior occurs. The value in result is ++ * only valid when the function returns false. ++ * ++ * Further information: ++ * https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow ++ */ ++ static bool add(T summand_1, T summand_2, T& result) ++ { ++ if (((summand_2 >= 0) && (summand_1 > std::numeric_limits::max() - summand_2)) || ++ ((summand_2 < 0) && (summand_1 < std::numeric_limits::min() - summand_2))) { ++ return true; ++ } else { ++ result = summand_1 + summand_2; ++ return false; ++ } ++ } ++ }; ++ ++ /*! ++ * @brief Overload of fallback_add_overflow for unsigned integers ++ */ ++ template ++ struct fallback_add_overflow::VALUE>::type> ++ { ++ /*! ++ * @brief Adds the two summands only if no overflow occurs ++ * ++ * This function performs a check if summand_1 + summand_2 would ++ * overflow and returns true in that case. If no overflow occurs, ++ * the sum is saved in result and false is returned. ++ * ++ * @return true on overflow, false on no overflow ++ * ++ * Further information: ++ * https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap ++ */ ++ static bool add(T summand_1, T summand_2, T& result) ++ { ++ if (summand_1 > std::numeric_limits::max() - summand_2) { ++ return true; ++ } else { ++ result = summand_1 + summand_2; ++ return false; ++ } ++ } ++ }; ++ ++ /*! ++ * @brief Overflow checker using compiler intrinsics ++ * ++ * This struct provides an add function with the same interface & ++ * behavior as fallback_add_overload::add but it relies on compiler ++ * intrinsics instead. This version should be considerably faster than ++ * the fallback version as it can fully utilize available CPU ++ * instructions & the compiler's diagnostic. ++ * ++ * However, as some compilers don't provide intrinsics for certain ++ * types, the default implementation of add is the version from falback. ++ * ++ * The struct is explicitly specialized for each type via #ifdefs for ++ * each compiler. ++ */ ++ template ++ struct builtin_add_overflow ++ { ++ /*! ++ * @brief Add summand_1 and summand_2 and check for overflows. ++ * ++ * This is the default add() function that uses ++ * fallback_add_overflow::add(). All specializations must have ++ * exactly the same interface and behave the same way. ++ */ ++ static inline bool add(T summand_1, T summand_2, T& result) ++ { ++ return fallback_add_overflow::add(summand_1, summand_2, result); ++ } ++ }; ++ ++#if defined(__GNUC__) || defined(__clang__) ++ ++/*! ++ * This macro pastes a specialization of builtin_add_overflow using gcc's & ++ * clang's __builtin_(s/u)add(l)(l)_overlow() ++ * ++ * The add function is implemented by forwarding the parameters to the intrinsic ++ * and returning its value. ++ * ++ * The intrinsics are documented here: ++ * https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#Integer-Overflow-Builtins ++ */ ++#define SPECIALIZE_builtin_add_overflow(type, builtin_name) \ ++ template <> \ ++ struct builtin_add_overflow \ ++ { \ ++ static inline bool add(type summand_1, type summand_2, type& result) \ ++ { \ ++ return builtin_name(summand_1, summand_2, &result); \ ++ } \ ++ } ++ ++ SPECIALIZE_builtin_add_overflow(int, __builtin_sadd_overflow); ++ SPECIALIZE_builtin_add_overflow(long, __builtin_saddl_overflow); ++ SPECIALIZE_builtin_add_overflow(long long, __builtin_saddll_overflow); ++ ++ SPECIALIZE_builtin_add_overflow(unsigned int, __builtin_uadd_overflow); ++ SPECIALIZE_builtin_add_overflow(unsigned long, __builtin_uaddl_overflow); ++ SPECIALIZE_builtin_add_overflow(unsigned long long, __builtin_uaddll_overflow); ++ ++#undef SPECIALIZE_builtin_add_overflow ++ ++#elif defined(_MSC_VER) ++ ++/*! ++ * This macro pastes a specialization of builtin_add_overflow using MSVC's ++ * U(Int/Long/LongLong)Add. ++ * ++ * The add function is implemented by forwarding the parameters to the ++ * intrinsic. As MSVC's intrinsics return S_OK on success, this specialization ++ * returns whether the intrinsics return value does not equal S_OK. This ensures ++ * a uniform interface of the add function (false is returned when no overflow ++ * occurs, true on overflow). ++ * ++ * The intrinsics are documented here: ++ * https://msdn.microsoft.com/en-us/library/windows/desktop/ff516460(v=vs.85).aspx ++ */ ++#define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \ ++ template <> \ ++ struct builtin_add_overflow \ ++ { \ ++ static inline bool add(type summand_1, type summand_2, type& result) \ ++ { \ ++ return builtin_name(summand_1, summand_2, &result) != S_OK; \ ++ } \ ++ } ++ ++ SPECIALIZE_builtin_add_overflow_WIN(unsigned int, UIntAdd); ++ SPECIALIZE_builtin_add_overflow_WIN(unsigned long, ULongAdd); ++ SPECIALIZE_builtin_add_overflow_WIN(unsigned long long, ULongLongAdd); ++ ++#undef SPECIALIZE_builtin_add_overflow_WIN ++ ++#endif ++ ++ } // namespace Internal ++ ++ /*! ++ * @brief Safe addition, throws an exception on overflow. ++ * ++ * This function returns the result of summand_1 and summand_2 only when the ++ * operation would not overflow, otherwise an exception of type ++ * std::overflow_error is thrown. ++ * ++ * @param[in] summand_1, summand_2 summands to be summed up ++ * @return the sum of summand_1 and summand_2 ++ * @throws std::overflow_error if the addition would overflow ++ * ++ * This function utilizes compiler builtins when available and should have a ++ * very small performance hit then. When builtins are unavailable, a more ++ * extensive check is required. ++ * ++ * Builtins are available for the following configurations: ++ * - GCC/Clang for signed and unsigned int, long and long long (not char & short) ++ * - MSVC for unsigned int, long and long long ++ */ ++ template ++ T add(T summand_1, T summand_2) ++ { ++ T res = 0; ++ if (Internal::builtin_add_overflow::add(summand_1, summand_2, res)) { ++ throw std::overflow_error("Overflow in addition"); ++ } ++ return res; ++ } ++ ++} // namespace Safe ++ ++#endif // SAFE_OP_HPP_ diff --git a/exiv2-CVE-2018-10958.patch b/exiv2-CVE-2018-10958.patch new file mode 100644 index 0000000..229b569 --- /dev/null +++ b/exiv2-CVE-2018-10958.patch @@ -0,0 +1,344 @@ +diff --git a/include/exiv2/error.hpp b/include/exiv2/error.hpp +index 24a70bf6..cc67725b 100644 +--- a/include/exiv2/error.hpp ++++ b/include/exiv2/error.hpp +@@ -192,6 +192,74 @@ namespace Exiv2 { + return os << error.what(); + } + ++ //! Complete list of all Exiv2 error codes ++ enum ErrorCode { ++ kerGeneralError = -1, ++ kerSuccess = 0, ++ kerErrorMessage, ++ kerCallFailed, ++ kerNotAnImage, ++ kerInvalidDataset, ++ kerInvalidRecord, ++ kerInvalidKey, ++ kerInvalidTag, ++ kerValueNotSet, ++ kerDataSourceOpenFailed, ++ kerFileOpenFailed, ++ kerFileContainsUnknownImageType, ++ kerMemoryContainsUnknownImageType, ++ kerUnsupportedImageType, ++ kerFailedToReadImageData, ++ kerNotAJpeg, ++ kerFailedToMapFileForReadWrite, ++ kerFileRenameFailed, ++ kerTransferFailed, ++ kerMemoryTransferFailed, ++ kerInputDataReadFailed, ++ kerImageWriteFailed, ++ kerNoImageInInputData, ++ kerInvalidIfdId, ++ //! Entry::setValue: Value too large ++ kerValueTooLarge, ++ //! Entry::setDataArea: Value too large ++ kerDataAreaValueTooLarge, ++ kerOffsetOutOfRange, ++ kerUnsupportedDataAreaOffsetType, ++ kerInvalidCharset, ++ kerUnsupportedDateFormat, ++ kerUnsupportedTimeFormat, ++ kerWritingImageFormatUnsupported, ++ kerInvalidSettingForImage, ++ kerNotACrwImage, ++ kerFunctionNotSupported, ++ kerNoNamespaceInfoForXmpPrefix, ++ kerNoPrefixForNamespace, ++ kerTooLargeJpegSegment, ++ kerUnhandledXmpdatum, ++ kerUnhandledXmpNode, ++ kerXMPToolkitError, ++ kerDecodeLangAltPropertyFailed, ++ kerDecodeLangAltQualifierFailed, ++ kerEncodeLangAltPropertyFailed, ++ kerPropertyNameIdentificationFailed, ++ kerSchemaNamespaceNotRegistered, ++ kerNoNamespaceForPrefix, ++ kerAliasesNotSupported, ++ kerInvalidXmpText, ++ kerTooManyTiffDirectoryEntries, ++ kerMultipleTiffArrayElementTagsInDirectory, ++ kerWrongTiffArrayElementTagType, ++ kerInvalidKeyXmpValue, ++ kerInvalidIccProfile, ++ kerInvalidXMP, ++ kerTiffDirectoryTooLarge, ++ kerInvalidTypeValue, ++ kerInvalidMalloc, ++ kerCorruptedMetadata, ++ kerArithmeticOverflow, ++ kerMallocFailed, ++ }; ++ + /*! + @brief Simple error class used for exceptions. An output operator is + provided to print errors to a stream. + +diff --git a/src/enforce.hpp b/src/enforce.hpp +new file mode 100644 +index 00000000..b2d77eea +--- /dev/null ++++ b/src/enforce.hpp +@@ -0,0 +1,96 @@ ++// ********************************************************* -*- C++ -*- ++/* ++ * Copyright (C) 2004-2018 Exiv2 maintainers ++ * ++ * This program is part of the Exiv2 distribution. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. ++ */ ++/*! ++ @file enforce.hpp ++ @brief Port of D's enforce() to C++ & Exiv2 ++ @author Dan Čermák (D4N) ++ dan.cermak@cgc-instruments.com ++ @date 11-March-18, D4N: created ++ */ ++ ++#include ++ ++#include "error.hpp" ++ ++/*! ++ * @brief Ensure that condition is true, otherwise throw an exception of the ++ * type exception_t ++ * ++ * @tparam exception_t Exception type that is thrown, must provide a ++ * constructor that accepts a single argument to which arg1 is forwarded. ++ * ++ * @todo once we have C++>=11 use variadic templates and std::forward to remove ++ * all overloads of enforce ++ */ ++template ++inline void enforce(bool condition, const T& arg1) ++{ ++ if (!condition) { ++ throw exception_t(arg1); ++ } ++} ++ ++/*! ++ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with ++ * the given error_code. ++ */ ++inline void enforce(bool condition, Exiv2::ErrorCode err_code) ++{ ++ if (!condition) { ++ throw Exiv2::Error(err_code); ++ } ++} ++ ++/*! ++ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with ++ * the given error_code & arg1. ++ */ ++template ++inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1) ++{ ++ if (!condition) { ++ throw Exiv2::Error(err_code, arg1); ++ } ++} ++ ++/*! ++ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with ++ * the given error_code, arg1 & arg2. ++ */ ++template ++inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2) ++{ ++ if (!condition) { ++ throw Exiv2::Error(err_code, arg1, arg2); ++ } ++} ++ ++/*! ++ * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with ++ * the given error_code, arg1, arg2 & arg3. ++ */ ++template ++inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2, const V& arg3) ++{ ++ if (!condition) { ++ throw Exiv2::Error(err_code, arg1, arg2, arg3); ++ } ++} + +diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp +index 4dcca4d..aae0f5f 100644 +--- a/src/pngchunk.cpp ++++ b/src/pngchunk.cpp +@@ -37,6 +37,7 @@ EXIV2_RCSID("@(#) $Id$") + #include "iptc.hpp" + #include "image.hpp" + #include "error.hpp" ++#include "enforce.hpp" + + // + standard includes + #include +@@ -46,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$") + #include + #include + #include ++#include + + #include // To uncompress or compress text chunk + +@@ -86,7 +88,7 @@ namespace Exiv2 { + + #ifdef DEBUG + std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk data: " +- << std::string((const char*)arr.pData_, arr.size_) << "\n"; ++ << std::string((const char*)arr.pData_, arr.size_) << std::endl; + #endif + parseChunkContent(pImage, key.pData_, key.size_, arr); + +@@ -99,7 +101,7 @@ namespace Exiv2 { + + #ifdef DEBUG + std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk key: " +- << std::string((const char*)key.pData_, key.size_) << "\n"; ++ << std::string((const char*)key.pData_, key.size_) << std::endl; + #endif + return parseTXTChunk(data, key.size_, type); + +@@ -164,12 +166,18 @@ namespace Exiv2 { + } + else if(type == iTXt_Chunk) + { ++ const int 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; ++ const byte compressionFlag = data.pData_[keysize + 1]; + // we get the compression method after the compression flag +- const byte* compressionMethod = data.pData_ + keysize + 2; ++ 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 + std::string languageText((const char*)(data.pData_ + keysize + 3)); + unsigned int languageTextSize = static_cast(languageText.size()); +@@ -177,7 +185,7 @@ namespace Exiv2 { + std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1)); + unsigned int translatedKeyTextSize = static_cast(translatedKeyText.size()); + +- if ( compressionFlag[0] == 0x00 ) ++ if ( compressionFlag == 0x00 ) + { + // then it's an uncompressed iTXt chunk + #ifdef DEBUG +@@ -191,7 +199,7 @@ namespace Exiv2 { + arr.alloc(textsize); + arr = DataBuf(text, textsize); + } +- else if ( compressionFlag[0] == 0x01 && compressionMethod[0] == 0x00 ) ++ else if ( compressionFlag == 0x01 && compressionMethod == 0x00 ) + { + // then it's a zlib compressed iTXt chunk + #ifdef DEBUG +diff --git a/src/pngimage.cpp b/src/pngimage.cpp +index ed7399a..991da6c 100644 +--- a/src/pngimage.cpp ++++ b/src/pngimage.cpp +@@ -375,7 +375,7 @@ namespace Exiv2 { + void PngImage::readMetadata() + { + #ifdef DEBUG +- std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << "\n"; ++ std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << std::endl; + #endif + if (io_->open() != 0) + { +@@ -398,7 +398,7 @@ namespace Exiv2 { + // Read chunk header. + + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << "\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << std::endl; + #endif + std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); + long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); +@@ -432,14 +432,14 @@ namespace Exiv2 { + { + // Last chunk found: we stop parsing. + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk (length: " << dataOffset << ")\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk with length: " << dataOffset << std::endl; + #endif + return; + } + else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4)) + { + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk (length: " << dataOffset << ")\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk with length: " << dataOffset << std::endl; + #endif + if (cdataBuf.size_ >= 8) { + PngChunk::decodeIHDRChunk(cdataBuf, &pixelWidth_, &pixelHeight_); +@@ -448,21 +448,21 @@ namespace Exiv2 { + else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4)) + { + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk (length: " << dataOffset << ")\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk with length: " << dataOffset << std::endl; + #endif + PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::tEXt_Chunk); + } + else if (!memcmp(cheaderBuf.pData_ + 4, "zTXt", 4)) + { + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk (length: " << dataOffset << ")\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk with length: " << dataOffset << std::endl; + #endif + PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::zTXt_Chunk); + } + else if (!memcmp(cheaderBuf.pData_ + 4, "iTXt", 4)) + { + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk (length: " << dataOffset << ")\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk with length: " << dataOffset << std::endl; + #endif + PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::iTXt_Chunk); + } +@@ -481,7 +481,7 @@ namespace Exiv2 { + + // Move to the next chunk: chunk data size + 4 CRC bytes. + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << "\n"; ++ std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << std::endl; + #endif + io_->seek(dataOffset + 4 , BasicIo::cur); + if (io_->error() || io_->eof()) throw Error(14); +@@ -511,8 +511,8 @@ namespace Exiv2 { + if (!outIo.isopen()) throw Error(21); + + #ifdef DEBUG +- std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n"; +- std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; ++ std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << std::endl; ++ std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << std::endl; + #endif + + // Ensure that this is the correct image type diff --git a/exiv2-CVE-2018-10998.patch b/exiv2-CVE-2018-10998.patch new file mode 100644 index 0000000..243b2c6 --- /dev/null +++ b/exiv2-CVE-2018-10998.patch @@ -0,0 +1,61 @@ +diff --git a/src/exiv2.cpp b/src/exiv2.cpp +index d6a45e1..dbd2834 100644 +--- a/src/exiv2.cpp ++++ b/src/exiv2.cpp +@@ -150,31 +150,35 @@ int main(int argc, char* const argv[]) + return 0; + } + +- // Create the required action class +- Action::TaskFactory& taskFactory = Action::TaskFactory::instance(); +- Action::Task::AutoPtr task +- = taskFactory.create(Action::TaskType(params.action_)); +- assert(task.get()); +- +- // Process all files + int rc = 0; +- int n = 1; +- int s = static_cast(params.files_.size()); +- int w = s > 9 ? s > 99 ? 3 : 2 : 1; +- for (Params::Files::const_iterator i = params.files_.begin(); +- i != params.files_.end(); ++i) { +- if (params.verbose_) { +- std::cout << _("File") << " " << std::setw(w) << std::right << n++ << "/" << s << ": " +- << *i << std::endl; ++ try { ++ // Create the required action class ++ Action::TaskFactory& taskFactory = Action::TaskFactory::instance(); ++ Action::Task::AutoPtr task = taskFactory.create(Action::TaskType(params.action_)); ++ assert(task.get()); ++ ++ // Process all files ++ int n = 1; ++ int s = static_cast(params.files_.size()); ++ int w = s > 9 ? s > 99 ? 3 : 2 : 1; ++ for (Params::Files::const_iterator i = params.files_.begin(); i != params.files_.end(); ++i) { ++ if (params.verbose_) { ++ std::cout << _("File") << " " << std::setw(w) << std::right << n++ << "/" << s << ": " << *i ++ << std::endl; ++ } ++ int ret = task->run(*i); ++ if (rc == 0) ++ rc = ret; + } +- int ret = task->run(*i); +- if (rc == 0) rc = ret; +- } + +- taskFactory.cleanup(); +- params.cleanup(); +- Exiv2::XmpParser::terminate(); ++ taskFactory.cleanup(); ++ params.cleanup(); ++ Exiv2::XmpParser::terminate(); + ++ } catch (const std::exception& exc) { ++ std::cerr << "Uncaught exception: " << exc.what() << std::endl; ++ rc = 1; ++ } + // Return a positive one byte code for better consistency across platforms + return static_cast(rc) % 256; + } // main diff --git a/exiv2-CVE-2018-11531.patch b/exiv2-CVE-2018-11531.patch new file mode 100644 index 0000000..5721e16 --- /dev/null +++ b/exiv2-CVE-2018-11531.patch @@ -0,0 +1,31 @@ +diff --git a/src/preview.cpp b/src/preview.cpp +index c34c8bd..69f8e01 100644 +--- a/src/preview.cpp ++++ b/src/preview.cpp +@@ -36,6 +36,7 @@ EXIV2_RCSID("@(#) $Id$") + + #include "preview.hpp" + #include "futils.hpp" ++#include "enforce.hpp" + + #include "image.hpp" + #include "cr2image.hpp" +@@ -807,13 +808,14 @@ namespace { + else { + // FIXME: the buffer is probably copied twice, it should be optimized + DataBuf buf(size_); +- Exiv2::byte* pos = buf.pData_; ++ uint32_t idxBuf = 0; + for (int i = 0; i < sizes.count(); i++) { + uint32_t offset = dataValue.toLong(i); + uint32_t size = sizes.toLong(i); +- if (offset + size <= static_cast(io.size())) +- memcpy(pos, base + offset, size); +- pos += size; ++ enforce(idxBuf + size < size_, kerCorruptedMetadata); ++ if (size!=0 && offset + size <= static_cast(io.size())) ++ memcpy(&buf.pData_[idxBuf], base + offset, size); ++ idxBuf += size; + } + dataValue.setDataArea(buf.pData_, buf.size_); + } diff --git a/exiv2-CVE-2018-12264-CVE-2018-12265.patch b/exiv2-CVE-2018-12264-CVE-2018-12265.patch new file mode 100644 index 0000000..593f8c9 --- /dev/null +++ b/exiv2-CVE-2018-12264-CVE-2018-12265.patch @@ -0,0 +1,60 @@ +diff --git a/src/preview.cpp b/src/preview.cpp +index 69f8e01..d20de04 100644 +--- a/src/preview.cpp ++++ b/src/preview.cpp +@@ -37,6 +37,7 @@ EXIV2_RCSID("@(#) $Id$") + #include "preview.hpp" + #include "futils.hpp" + #include "enforce.hpp" ++#include "safe_op.hpp" + + #include "image.hpp" + #include "cr2image.hpp" +@@ -386,7 +387,7 @@ namespace { + return AutoPtr(); + + if (loaderList_[id].imageMimeType_ && +- std::string(loaderList_[id].imageMimeType_) != std::string(image.mimeType())) ++ std::string(loaderList_[id].imageMimeType_) != image.mimeType()) + return AutoPtr(); + + AutoPtr loader = loaderList_[id].create_(id, image, loaderList_[id].parIdx_); +@@ -548,7 +549,8 @@ namespace { + } + } + +- if (offset_ + size_ > static_cast(image_.io().size())) return; ++ if (Safe::add(offset_, size_) > static_cast(image_.io().size())) ++ return; + + valid_ = true; + } +@@ -802,7 +804,7 @@ namespace { + // this saves one copying of the buffer + uint32_t offset = dataValue.toLong(0); + uint32_t size = sizes.toLong(0); +- if (offset + size <= static_cast(io.size())) ++ if (Safe::add(offset, size) <= static_cast(io.size())) + dataValue.setDataArea(base + offset, size); + } + else { +@@ -812,8 +814,8 @@ namespace { + for (int i = 0; i < sizes.count(); i++) { + uint32_t offset = dataValue.toLong(i); + uint32_t size = sizes.toLong(i); +- enforce(idxBuf + size < size_, kerCorruptedMetadata); +- if (size!=0 && offset + size <= static_cast(io.size())) ++ enforce(Safe::add(idxBuf, size) < size_, kerCorruptedMetadata); ++ if (size!=0 && Safe::add(offset, size) <= static_cast(io.size())) + memcpy(&buf.pData_[idxBuf], base + offset, size); + idxBuf += size; + } +@@ -930,7 +932,7 @@ namespace { + + DataBuf decodeBase64(const std::string& src) + { +- const unsigned long srcSize = static_cast(src.size()); ++ const unsigned long srcSize = src.size(); + + // create decoding table + unsigned long invalid = 64; diff --git a/exiv2-CVE-2018-14046.patch b/exiv2-CVE-2018-14046.patch new file mode 100644 index 0000000..d5ce560 --- /dev/null +++ b/exiv2-CVE-2018-14046.patch @@ -0,0 +1,49 @@ +diff --git a/src/webpimage.cpp b/src/webpimage.cpp +index e4057d6..f1dd77c 100644 +--- a/src/webpimage.cpp ++++ b/src/webpimage.cpp +@@ -44,6 +44,8 @@ + #include "tiffimage.hpp" + #include "tiffimage_int.hpp" + #include "convert.hpp" ++#include "enforce.hpp" ++ + #include + #include + #include +@@ -516,6 +518,8 @@ namespace Exiv2 { + DataBuf payload(size); + + if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8X) && !has_canvas_data) { ++ enforce(size >= 10, Exiv2::kerCorruptedMetadata); ++ + has_canvas_data = true; + byte size_buf[WEBP_TAG_SIZE]; + +@@ -531,6 +535,8 @@ namespace Exiv2 { + size_buf[3] = 0; + pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1; + } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8) && !has_canvas_data) { ++ enforce(size >= 10, Exiv2::kerCorruptedMetadata); ++ + has_canvas_data = true; + io_->read(payload.pData_, payload.size_); + byte size_buf[WEBP_TAG_SIZE]; +@@ -547,6 +553,8 @@ namespace Exiv2 { + size_buf[3] = 0; + pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff; + } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_VP8L) && !has_canvas_data) { ++ enforce(size >= 5, Exiv2::kerCorruptedMetadata); ++ + has_canvas_data = true; + byte size_buf_w[2]; + byte size_buf_h[3]; +@@ -564,6 +572,8 @@ namespace Exiv2 { + size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0xF) << 0x2); + pixelHeight_ = Exiv2::getUShort(size_buf_h, littleEndian) + 1; + } else if (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_ANMF) && !has_canvas_data) { ++ enforce(size >= 12, Exiv2::kerCorruptedMetadata); ++ + has_canvas_data = true; + byte size_buf[WEBP_TAG_SIZE]; + diff --git a/exiv2-CVE-2018-5772.patch b/exiv2-CVE-2018-5772.patch new file mode 100644 index 0000000..d1fbdf3 --- /dev/null +++ b/exiv2-CVE-2018-5772.patch @@ -0,0 +1,76 @@ +diff --git a/src/cr2image.cpp b/src/cr2image.cpp +index 2907426..b6fa315 100644 +--- a/src/cr2image.cpp ++++ b/src/cr2image.cpp +@@ -107,8 +107,6 @@ namespace Exiv2 { + throw Error(3, "CR2"); + } + clearMetadata(); +- std::ofstream devnull; +- printStructure(devnull, kpsRecursive, 0); + ByteOrder bo = Cr2Parser::decode(exifData_, + iptcData_, + xmpData_, +diff --git a/src/crwimage.cpp b/src/crwimage.cpp +index ca79aa7..11cd14c 100644 +--- a/src/crwimage.cpp ++++ b/src/crwimage.cpp +@@ -131,15 +131,8 @@ namespace Exiv2 { + throw Error(33); + } + clearMetadata(); +- // read all metadata into memory +- // we should put this into clearMetadata(), however it breaks the test suite! +- try { +- std::ofstream devnull; +- printStructure(devnull,kpsRecursive,0); +- } catch (Exiv2::Error& /* e */) { +- DataBuf file(io().size()); +- io_->read(file.pData_,file.size_); +- } ++ DataBuf file( (long) io().size()); ++ io_->read(file.pData_,file.size_); + + CrwParser::decode(this, io_->mmap(), io_->size()); + +diff --git a/src/orfimage.cpp b/src/orfimage.cpp +index c516591..9a17a50 100644 +--- a/src/orfimage.cpp ++++ b/src/orfimage.cpp +@@ -119,8 +119,6 @@ namespace Exiv2 { + throw Error(3, "ORF"); + } + clearMetadata(); +- std::ofstream devnull; +- printStructure(devnull, kpsRecursive, 0); + ByteOrder bo = OrfParser::decode(exifData_, + iptcData_, + xmpData_, +diff --git a/src/rw2image.cpp b/src/rw2image.cpp +index 95f3b28..764de6f 100644 +--- a/src/rw2image.cpp ++++ b/src/rw2image.cpp +@@ -130,8 +130,6 @@ namespace Exiv2 { + throw Error(3, "RW2"); + } + clearMetadata(); +- std::ofstream devnull; +- printStructure(devnull, kpsRecursive, 0); + ByteOrder bo = Rw2Parser::decode(exifData_, + iptcData_, + xmpData_, +diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp +index f20c69e..9e6eda4 100644 +--- a/src/tiffimage.cpp ++++ b/src/tiffimage.cpp +@@ -185,10 +185,6 @@ namespace Exiv2 { + } + clearMetadata(); + +- // recursively print the structure to /dev/null to ensure all metadata is in memory +- // must be recursive to handle NEFs which stores the raw image in a subIFDs +- std::ofstream devnull; +- printStructure(devnull,kpsRecursive,0); + ByteOrder bo = TiffParser::decode(exifData_, + iptcData_, + xmpData_, diff --git a/exiv2-CVE-2018-8976.patch b/exiv2-CVE-2018-8976.patch new file mode 100644 index 0000000..ebfafc5 --- /dev/null +++ b/exiv2-CVE-2018-8976.patch @@ -0,0 +1,466 @@ +diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp +index 9afcb58..ca83f14 100644 +--- a/src/jpgimage.cpp ++++ b/src/jpgimage.cpp +@@ -34,6 +34,7 @@ EXIV2_RCSID("@(#) $Id$") + #include "image_int.hpp" + #include "error.hpp" + #include "futils.hpp" ++#include "enforce.hpp" + + #ifdef WIN32 + #include +@@ -328,12 +329,14 @@ namespace Exiv2 { + int c = -1; + // Skips potential padding between markers + while ((c=io_->getb()) != 0xff) { +- if (c == EOF) return -1; ++ if (c == EOF) ++ return -1; + } + + // Markers can start with any number of 0xff + while ((c=io_->getb()) == 0xff) { +- if (c == EOF) return -2; ++ if (c == EOF) ++ return -2; + } + return c; + } +@@ -564,85 +567,88 @@ namespace Exiv2 { + out << Internal::stringFormat("%8ld | 0xff%02x %-5s", \ + io_->tell()-2,marker,nm[marker].c_str()) + +- void JpegBase::printStructure(std::ostream& out, PrintStructureOption option,int depth) ++ void JpegBase::printStructure(std::ostream& out, PrintStructureOption option, int depth) + { +- if (io_->open() != 0) throw Error(9, io_->path(), strError()); ++ if (io_->open() != 0) ++ throw Error(9, io_->path(), strError()); + // Ensure that this is the correct image type + if (!isThisType(*io_, false)) { +- if (io_->error() || io_->eof()) throw Error(14); ++ if (io_->error() || io_->eof()) ++ throw Error(14); + throw Error(15); + } + +- bool bPrint = option==kpsBasic || option==kpsRecursive; ++ bool bPrint = option == kpsBasic || option == kpsRecursive; + Exiv2::Uint32Vector iptcDataSegs; + +- if ( bPrint || option == kpsXMP || option == kpsIccProfile || option == kpsIptcErase ) { ++ if (bPrint || option == kpsXMP || option == kpsIccProfile || option == kpsIptcErase) { + + // nmonic for markers +- std::string nm[256] ; +- nm[0xd8]="SOI" ; +- nm[0xd9]="EOI" ; +- nm[0xda]="SOS" ; +- nm[0xdb]="DQT" ; +- nm[0xdd]="DRI" ; +- nm[0xfe]="COM" ; ++ std::string nm[256]; ++ nm[0xd8] = "SOI"; ++ nm[0xd9] = "EOI"; ++ nm[0xda] = "SOS"; ++ nm[0xdb] = "DQT"; ++ nm[0xdd] = "DRI"; ++ nm[0xfe] = "COM"; + + // 0xe0 .. 0xef are APPn + // 0xc0 .. 0xcf are SOFn (except 4) +- nm[0xc4]="DHT" ; +- for ( int i = 0 ; i <= 15 ; i++ ) { ++ nm[0xc4] = "DHT"; ++ for (int i = 0; i <= 15; i++) { + char MN[10]; +- sprintf(MN,"APP%d",i); +- nm[0xe0+i] = MN; +- if ( i != 4 ) { +- sprintf(MN,"SOF%d",i); +- nm[0xc0+i] = MN; ++ sprintf(MN, "APP%d", i); ++ nm[0xe0 + i] = MN; ++ if (i != 4) { ++ sprintf(MN, "SOF%d", i); ++ nm[0xc0 + i] = MN; + } + } + + // which markers have a length field? + bool mHasLength[256]; +- for ( int i = 0 ; i < 256 ; i ++ ) +- mHasLength[i] +- = ( i >= sof0_ && i <= sof15_) +- || ( i >= app0_ && i <= (app0_ | 0x0F)) +- || ( i == dht_ || i == dqt_ || i == dri_ || i == com_ || i == sos_ ) +- ; ++ for (int i = 0; i < 256; i++) ++ mHasLength[i] = (i >= sof0_ && i <= sof15_) || (i >= app0_ && i <= (app0_ | 0x0F)) || ++ (i == dht_ || i == dqt_ || i == dri_ || i == com_ || i == sos_); + + // Container for the signature +- bool bExtXMP = false; +- long bufRead = 0; +- const long bufMinSize = 36; +- DataBuf buf(bufMinSize); ++ bool bExtXMP = false; ++ long bufRead = 0; ++ const long bufMinSize = 36; ++ DataBuf buf(bufMinSize); + + // Read section marker + int marker = advanceToMarker(); +- if (marker < 0) throw Error(15); ++ if (marker < 0) ++ throw Error(15); + +- bool done = false; +- bool first= true; ++ bool done = false; ++ bool first = true; + while (!done) { + // print marker bytes +- if ( first && bPrint ) { ++ if (first && bPrint) { + out << "STRUCTURE OF JPEG FILE: " << io_->path() << std::endl; +- out << " address | marker | length | data" << std::endl ; ++ out << " address | marker | length | data" << std::endl; + REPORT_MARKER; + } +- first = false; ++ first = false; + bool bLF = bPrint; + + // Read size and signature + std::memset(buf.pData_, 0x0, buf.size_); + bufRead = io_->read(buf.pData_, bufMinSize); +- if (io_->error()) throw Error(14); +- if (bufRead < 2) throw Error(15); +- uint16_t size = mHasLength[marker] ? getUShort(buf.pData_, bigEndian) : 0 ; +- if ( bPrint && mHasLength[marker] ) out << Internal::stringFormat(" | %7d ", size); ++ if (io_->error()) ++ throw Error(14); ++ if (bufRead < 2) ++ throw Error(15); ++ uint16_t size = mHasLength[marker] ? getUShort(buf.pData_, bigEndian) : 0; ++ if (bPrint && mHasLength[marker]) ++ out << Internal::stringFormat(" | %7d ", size); + + // print signature for APPn + if (marker >= app0_ && marker <= (app0_ | 0x0F)) { + // http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf p75 +- const char* signature = (const char*) buf.pData_+2; ++ const char* signature = (const char*)buf.pData_ + 2; + + // 728 rmills@rmillsmbp:~/gnu/exiv2/ttt $ exiv2 -pS test/data/exiv2-bug922.jpg + // STRUCTURE OF JPEG FILE: test/data/exiv2-bug922.jpg +@@ -651,13 +657,13 @@ namespace Exiv2 { + // 2 | 0xe1 APP1 | 911 | Exif..MM.*.......%.........#.... + // 915 | 0xe1 APP1 | 870 | http://ns.adobe.com/xap/1.0/. 0 ) { +- io_->seek(-bufRead , BasicIo::cur); +- byte* xmp = new byte[size+1]; +- io_->read(xmp,size); +- int start = 0 ; ++ if (size > 0) { ++ io_->seek(-bufRead, BasicIo::cur); ++ byte* xmp = new byte[size + 1]; ++ io_->read(xmp, size); ++ int start = 0; + + // http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf + // if we find HasExtendedXMP, set the flag and ignore this block +@@ -666,79 +672,80 @@ namespace Exiv2 { + // we could implement out of sequence with a dictionary of sequence/offset + // and dumping the XMP in a post read operation similar to kpsIptcErase + // for the moment, dumping 'on the fly' is working fine +- if ( ! bExtXMP ) { +- while (xmp[start]) start++; ++ if (!bExtXMP) { ++ while (xmp[start]) ++ start++; + start++; +- if ( ::strstr((char*)xmp+start,"HasExtendedXMP") ) { +- start = size ; // ignore this packet, we'll get on the next time around ++ if (::strstr((char*)xmp + start, "HasExtendedXMP")) { ++ start = size; // ignore this packet, we'll get on the next time around + bExtXMP = true; + } + } else { +- start = 2+35+32+4+4; // Adobe Spec, p19 ++ start = 2 + 35 + 32 + 4 + 4; // Adobe Spec, p19 + } + +- out.write((const char*)(xmp+start),size-start); +- delete [] xmp; ++ out.write((const char*)(xmp + start), size - start); ++ delete[] xmp; + bufRead = size; + done = !bExtXMP; + } +- } else if ( option == kpsIccProfile && std::strcmp(signature,iccId_) == 0 ) { ++ } else if (option == kpsIccProfile && std::strcmp(signature, iccId_) == 0) { + // extract ICCProfile +- if ( size > 0 ) { +- io_->seek(-bufRead, BasicIo::cur); // back to buffer (after marker) +- io_->seek( 14+2, BasicIo::cur); // step over header +- DataBuf icc(size-(14+2)); +- io_->read( icc.pData_,icc.size_); +- out.write((const char*)icc.pData_,icc.size_); ++ if (size > 0) { ++ io_->seek(-bufRead, BasicIo::cur); // back to buffer (after marker) ++ io_->seek(14 + 2, BasicIo::cur); // step over header ++ DataBuf icc(size - (14 + 2)); ++ io_->read(icc.pData_, icc.size_); ++ out.write((const char*)icc.pData_, icc.size_); + #ifdef DEBUG + std::cout << "iccProfile size = " << icc.size_ << std::endl; + #endif + bufRead = size; + } +- } else if ( option == kpsIptcErase && std::strcmp(signature,"Photoshop 3.0") == 0 ) { ++ } else if (option == kpsIptcErase && std::strcmp(signature, "Photoshop 3.0") == 0) { + // delete IPTC data segment from JPEG +- if ( size > 0 ) { +- io_->seek(-bufRead , BasicIo::cur); ++ if (size > 0) { ++ io_->seek(-bufRead, BasicIo::cur); + iptcDataSegs.push_back(io_->tell()); + iptcDataSegs.push_back(size); + } +- } else if ( bPrint ) { +- out << "| " << Internal::binaryToString(buf,size>32?32:size,size>0?2:0); +- if ( std::strcmp(signature,iccId_) == 0 ) { +- int chunk = (int) signature[12]; +- int chunks = (int) signature[13]; +- out << Internal::stringFormat(" chunk %d/%d",chunk,chunks); ++ } else if (bPrint) { ++ out << "| " << Internal::binaryToString(buf, size > 32 ? 32 : size, size > 0 ? 2 : 0); ++ if (std::strcmp(signature, iccId_) == 0) { ++ int chunk = (int)signature[12]; ++ int chunks = (int)signature[13]; ++ out << Internal::stringFormat(" chunk %d/%d", chunk, chunks); + } + } + + // for MPF: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html + // for FLIR: http://owl.phy.queensu.ca/~phil/exiftool/TagNames/FLIR.html +- bool bFlir = option == kpsRecursive && marker == (app0_+1) && std::strcmp(signature,"FLIR")==0; +- bool bExif = option == kpsRecursive && marker == (app0_+1) && std::strcmp(signature,"Exif")==0; +- bool bMPF = option == kpsRecursive && marker == (app0_+2) && std::strcmp(signature,"MPF")==0; +- bool bPS = option == kpsRecursive && std::strcmp(signature,"Photoshop 3.0")==0; +- if( bFlir || bExif || bMPF || bPS ) { ++ bool bFlir = option == kpsRecursive && marker == (app0_ + 1) && std::strcmp(signature, "FLIR") == 0; ++ bool bExif = option == kpsRecursive && marker == (app0_ + 1) && std::strcmp(signature, "Exif") == 0; ++ bool bMPF = option == kpsRecursive && marker == (app0_ + 2) && std::strcmp(signature, "MPF") == 0; ++ bool bPS = option == kpsRecursive && std::strcmp(signature, "Photoshop 3.0") == 0; ++ if (bFlir || bExif || bMPF || bPS) { + // extract Exif data block which is tiff formatted +- if ( size > 0 ) { ++ if (size > 0) { + out << std::endl; + + // allocate storage and current file position +- byte* exif = new byte[size]; +- uint32_t restore = io_->tell(); ++ byte* exif = new byte[size]; ++ uint32_t restore = io_->tell(); + + // copy the data to memory +- io_->seek(-bufRead , BasicIo::cur); +- io_->read(exif,size); +- uint32_t start = std::strcmp(signature,"Exif")==0 ? 8 : 6; +- uint32_t max = (uint32_t) size -1; ++ io_->seek(-bufRead, BasicIo::cur); ++ io_->read(exif, size); ++ uint32_t start = std::strcmp(signature, "Exif") == 0 ? 8 : 6; ++ uint32_t max = (uint32_t)size - 1; + + // is this an fff block? +- if ( bFlir ) { +- start = 0 ; ++ if (bFlir) { ++ start = 0; + bFlir = false; +- while ( start < max ) { +- if ( std::strcmp((const char*)(exif+start),"FFF")==0 ) { +- bFlir = true ; ++ while (start < max) { ++ if (std::strcmp((const char*)(exif + start), "FFF") == 0) { ++ bFlir = true; + break; + } + start++; +@@ -747,78 +754,90 @@ namespace Exiv2 { + + // there is a header in FLIR, followed by a tiff block + // Hunt down the tiff using brute force +- if ( bFlir ) { ++ if (bFlir) { + // FLIRFILEHEAD* pFFF = (FLIRFILEHEAD*) (exif+start) ; +- while ( start < max ) { +- if ( exif[start] == 'I' && exif[start+1] == 'I' ) break; +- if ( exif[start] == 'M' && exif[start+1] == 'M' ) break; ++ while (start < max) { ++ if (exif[start] == 'I' && exif[start + 1] == 'I') ++ break; ++ if (exif[start] == 'M' && exif[start + 1] == 'M') ++ break; + start++; + } +- if ( start < max ) std::cout << " FFF start = " << start << std::endl ; ++ if ( start < max ) ++ std::cout << " FFF start = " << start << std::endl; + // << " index = " << pFFF->dwIndexOff << std::endl; + } + +- if ( bPS ) { +- IptcData::printStructure(out,exif,size,depth); ++ if (bPS) { ++ IptcData::printStructure(out, exif, size, depth); + } else { + // create a copy on write memio object with the data, then print the structure +- BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(exif+start,size-start)); +- if ( start < max ) printTiffStructure(*p,out,option,depth); ++ BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(exif + start, size - start)); ++ if (start < max) ++ printTiffStructure(*p, out, option, depth); + } + + // restore and clean up +- io_->seek(restore,Exiv2::BasicIo::beg); +- delete [] exif; +- bLF = false; ++ io_->seek(restore, Exiv2::BasicIo::beg); ++ delete[] exif; ++ bLF = false; + } + } + } + + // print COM marker +- if ( bPrint && marker == com_ ) { +- int n = (size-2)>32?32:size-2; // size includes 2 for the two bytes for size! +- out << "| " << Internal::binaryToString(buf,n,2); // start after the two bytes ++ if (bPrint && marker == com_) { ++ int n = (size - 2) > 32 ? 32 : size - 2; // size includes 2 for the two bytes for size! ++ out << "| " << Internal::binaryToString(buf, n, 2); // start after the two bytes + } + + // Skip the segment if the size is known +- if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14); ++ if (io_->seek(size - bufRead, BasicIo::cur)) ++ throw Error(14); + +- if ( bLF ) out << std::endl; ++ if (bLF) ++ out << std::endl; + + if (marker != sos_) { + // Read the beginning of the next segment + marker = advanceToMarker(); ++ enforce(marker>=0, kerNoImageInInputData); + REPORT_MARKER; + } + done |= marker == eoi_ || marker == sos_; +- if ( done && bPrint ) out << std::endl; ++ if (done && bPrint) ++ out << std::endl; + } + } +- if ( option == kpsIptcErase && iptcDataSegs.size() ) { ++ if (option == kpsIptcErase && iptcDataSegs.size()) { + #ifdef DEBUG + std::cout << "iptc data blocks: " << iptcDataSegs.size() << std::endl; +- uint32_t toggle = 0 ; +- for ( Uint32Vector_i i = iptcDataSegs.begin(); i != iptcDataSegs.end() ; i++ ) { +- std::cout << *i ; +- if ( toggle++ % 2 ) std::cout << std::endl; else std::cout << ' ' ; ++ uint32_t toggle = 0; ++ for (Uint32Vector_i i = iptcDataSegs.begin(); i != iptcDataSegs.end(); i++) { ++ std::cout << *i; ++ if (toggle++ % 2) ++ std::cout << std::endl; ++ else ++ std::cout << ' '; + } + #endif +- uint32_t count = (uint32_t) iptcDataSegs.size(); ++ uint32_t count = (uint32_t)iptcDataSegs.size(); + + // figure out which blocks to copy +- uint64_t* pos = new uint64_t[count+2]; +- pos[0] = 0 ; ++ uint64_t* pos = new uint64_t[count + 2]; ++ pos[0] = 0; + // copy the data that is not iptc + Uint32Vector_i it = iptcDataSegs.begin(); +- for ( uint64_t i = 0 ; i < count ; i++ ) { +- bool bOdd = (i%2)!=0; +- bool bEven = !bOdd; +- pos[i+1] = bEven ? *it : pos[i] + *it; ++ for (uint64_t i = 0; i < count; i++) { ++ bool bOdd = (i % 2) != 0; ++ bool bEven = !bOdd; ++ pos[i + 1] = bEven ? *it : pos[i] + *it; + it++; + } +- pos[count+1] = io_->size() - pos[count]; ++ pos[count + 1] = io_->size() - pos[count]; + #ifdef DEBUG +- for ( uint64_t i = 0 ; i < count+2 ; i++ ) std::cout << pos[i] << " " ; ++ for (uint64_t i = 0; i < count + 2; i++) ++ std::cout << pos[i] << " "; + std::cout << std::endl; + #endif + // $ dd bs=1 skip=$((0)) count=$((13164)) if=ETH0138028.jpg of=E1.jpg +@@ -829,29 +848,30 @@ namespace Exiv2 { + // binary copy io_ to a temporary file + BasicIo::AutoPtr tempIo(new MemIo); + +- assert (tempIo.get() != 0); +- for ( uint64_t i = 0 ; i < (count/2)+1 ; i++ ) { +- uint64_t start = pos[2*i]+2 ; // step JPG 2 byte marker +- if ( start == 2 ) start = 0 ; // read the file 2 byte SOI +- long length = (long) (pos[2*i+1] - start) ; +- if ( length ) { ++ assert(tempIo.get() != 0); ++ for (uint64_t i = 0; i < (count / 2) + 1; i++) { ++ uint64_t start = pos[2 * i] + 2; // step JPG 2 byte marker ++ if (start == 2) ++ start = 0; // read the file 2 byte SOI ++ long length = (long)(pos[2 * i + 1] - start); ++ if (length) { + #ifdef DEBUG +- std::cout << start <<":"<< length << std::endl; ++ std::cout << start << ":" << length << std::endl; + #endif +- io_->seek(start,BasicIo::beg); ++ io_->seek(start, BasicIo::beg); + DataBuf buf(length); +- io_->read(buf.pData_,buf.size_); +- tempIo->write(buf.pData_,buf.size_); ++ io_->read(buf.pData_, buf.size_); ++ tempIo->write(buf.pData_, buf.size_); + } + } +- delete [] pos; ++ delete[] pos; + + io_->seek(0, BasicIo::beg); +- io_->transfer(*tempIo); // may throw ++ io_->transfer(*tempIo); // may throw + io_->seek(0, BasicIo::beg); + readMetadata(); + } +- } // JpegBase::printStructure ++ } // JpegBase::printStructure + + void JpegBase::writeMetadata() + { diff --git a/exiv2-CVE-2018-8977.patch b/exiv2-CVE-2018-8977.patch new file mode 100644 index 0000000..fcb95f1 --- /dev/null +++ b/exiv2-CVE-2018-8977.patch @@ -0,0 +1,21 @@ +diff --git a/src/canonmn.cpp b/src/canonmn.cpp +index 450c7d9..f768c05 100644 +--- a/src/canonmn.cpp ++++ b/src/canonmn.cpp +@@ -1774,9 +1774,13 @@ namespace Exiv2 { + { + try { + // 1140 +- if( metadata->findKey(ExifKey("Exif.Image.Model" ))->value().toString() == "Canon EOS 30D" +- && metadata->findKey(ExifKey("Exif.CanonCs.Lens" ))->value().toString() == "24 24 1" +- && metadata->findKey(ExifKey("Exif.CanonCs.MaxAperture"))->value().toString() == "95" // F2.8 ++ const ExifData::const_iterator itModel = metadata->findKey(ExifKey("Exif.Image.Model")); ++ const ExifData::const_iterator itLens = metadata->findKey(ExifKey("Exif.CanonCs.Lens")); ++ const ExifData::const_iterator itApert = metadata->findKey(ExifKey("Exif.CanonCs.MaxAperture")); ++ ++ if( itModel != metadata->end() && itModel->value().toString() == "Canon EOS 30D" ++ && itLens != metadata->end() && itLens->value().toString() == "24 24 1" ++ && itApert != metadata->end() && itApert->value().toString() == "95" // F2.8 + ){ + return os << "Canon EF-S 24mm f/2.8 STM" ; + } diff --git a/exiv2-additional-security-fixes.patch b/exiv2-additional-security-fixes.patch new file mode 100644 index 0000000..30d1c41 --- /dev/null +++ b/exiv2-additional-security-fixes.patch @@ -0,0 +1,176 @@ +diff --git a/src/actions.cpp b/src/actions.cpp +index 0ebe850..3cd398e 100644 +--- a/src/actions.cpp ++++ b/src/actions.cpp +@@ -59,6 +59,7 @@ EXIV2_RCSID("@(#) $Id$") + #include + #include + #include ++#include + #include // for stat() + #include // for stat() + #ifdef EXV_HAVE_UNISTD_H +@@ -236,33 +237,43 @@ namespace Action { + } + + int Print::run(const std::string& path) +- try { +- path_ = path; +- int rc = 0; +- Exiv2::PrintStructureOption option = Exiv2::kpsNone ; +- switch (Params::instance().printMode_) { +- case Params::pmSummary: rc = printSummary(); break; +- case Params::pmList: rc = printList(); break; +- case Params::pmComment: rc = printComment(); break; +- case Params::pmPreview: rc = printPreviewList(); break; +- case Params::pmStructure: rc = printStructure(std::cout,Exiv2::kpsBasic) ; break; +- case Params::pmRecursive: rc = printStructure(std::cout,Exiv2::kpsRecursive) ; break; +- +- case Params::pmXMP: +- option = option == Exiv2::kpsNone ? Exiv2::kpsXMP : option; // drop +- case Params::pmIccProfile:{ +- option = option == Exiv2::kpsNone ? Exiv2::kpsIccProfile : option; +- _setmode(_fileno(stdout),O_BINARY); +- rc = printStructure(std::cout,option); +- } break; ++ { ++ try { ++ path_ = path; ++ int rc = 0; ++ Exiv2::PrintStructureOption option = Exiv2::kpsNone ; ++ switch (Params::instance().printMode_) { ++ case Params::pmSummary: rc = printSummary(); break; ++ case Params::pmList: rc = printList(); break; ++ case Params::pmComment: rc = printComment(); break; ++ case Params::pmPreview: rc = printPreviewList(); break; ++ case Params::pmStructure: rc = printStructure(std::cout,Exiv2::kpsBasic) ; break; ++ case Params::pmRecursive: rc = printStructure(std::cout,Exiv2::kpsRecursive) ; break; ++ ++ case Params::pmXMP: ++ if (option == Exiv2::kpsNone) ++ option = Exiv2::kpsXMP; ++ // drop ++ case Params::pmIccProfile: ++ if (option == Exiv2::kpsNone) ++ option = Exiv2::kpsIccProfile; ++ _setmode(_fileno(stdout),O_BINARY); ++ rc = printStructure(std::cout,option); ++ break; ++ } ++ return rc; + } +- return rc; +- } +- catch(const Exiv2::AnyError& e) { +- std::cerr << "Exiv2 exception in print action for file " +- << path << ":\n" << e << "\n"; +- return 1; +- } // Print::run ++ catch(const Exiv2::AnyError& e) { ++ std::cerr << "Exiv2 exception in print action for file " ++ << path << ":\n" << e << "\n"; ++ return 1; ++ } ++ catch(const std::overflow_error& e) { ++ std::cerr << "std::overflow_error exception in print action for file " ++ << path << ":\n" << e.what() << "\n"; ++ return 1; ++ } ++ } + + int Print::printStructure(std::ostream& out, Exiv2::PrintStructureOption option) + { +diff --git a/src/error.cpp b/src/error.cpp +index e90a9c0..5d63957 100644 +--- a/src/error.cpp ++++ b/src/error.cpp +@@ -109,6 +109,8 @@ namespace { + { 55, N_("tiff directory length is too large") }, + { 56, N_("invalid type value detected in Image::printIFDStructure") }, + { 57, N_("invalid memory allocation request") }, ++ { 58, N_("corrupted image metadata") }, ++ { 59, N_("Arithmetic operation overflow") }, + }; + + } +diff --git a/src/nikonmn.cpp b/src/nikonmn.cpp +index 571ab80..34bf601 100644 +--- a/src/nikonmn.cpp ++++ b/src/nikonmn.cpp +@@ -299,6 +299,8 @@ namespace Exiv2 { + const Value& value, + const ExifData* exifData) + { ++ if ( ! exifData ) return os << "undefined" ; ++ + if ( value.count() >= 9 ) { + ByteOrder bo = getKeyString("Exif.MakerNote.ByteOrder",exifData) == "MM" ? bigEndian : littleEndian; + byte p[4]; +diff --git a/src/pentaxmn.cpp b/src/pentaxmn.cpp +index 4fc38be..b22cb43 100644 +--- a/src/pentaxmn.cpp ++++ b/src/pentaxmn.cpp +@@ -1167,6 +1167,8 @@ namespace Exiv2 { + + std::ostream& PentaxMakerNote::printShutterCount(std::ostream& os, const Value& value, const ExifData* metadata) + { ++ if ( ! metadata ) return os << "undefined" ; ++ + ExifData::const_iterator dateIt = metadata->findKey( + ExifKey("Exif.PentaxDng.Date")); + if (dateIt == metadata->end()) { +diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp +index da4ccd0..4dcca4d 100644 +--- a/src/pngchunk.cpp ++++ b/src/pngchunk.cpp +@@ -68,6 +68,8 @@ namespace Exiv2 { + int* outWidth, + int* outHeight) + { ++ assert(data.size_ >= 8); ++ + // Extract image width and height from IHDR chunk. + + *outWidth = getLong((const byte*)data.pData_, bigEndian); +diff --git a/src/pngimage.cpp b/src/pngimage.cpp +index 11b4198..ed7399a 100644 +--- a/src/pngimage.cpp ++++ b/src/pngimage.cpp +@@ -441,7 +441,9 @@ namespace Exiv2 { + #ifdef DEBUG + std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk (length: " << dataOffset << ")\n"; + #endif +- PngChunk::decodeIHDRChunk(cdataBuf, &pixelWidth_, &pixelHeight_); ++ if (cdataBuf.size_ >= 8) { ++ PngChunk::decodeIHDRChunk(cdataBuf, &pixelWidth_, &pixelHeight_); ++ } + } + else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4)) + { +diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp +index 74f8d07..fad39b6 100644 +--- a/src/tiffvisitor.cpp ++++ b/src/tiffvisitor.cpp +@@ -1493,6 +1493,11 @@ namespace Exiv2 { + } + p += 4; + uint32_t isize= 0; // size of Exif.Sony1.PreviewImage ++ ++ if (count > std::numeric_limits::max() / typeSize) { ++ throw Error(59); ++ } ++ + uint32_t size = typeSize * count; + uint32_t offset = getLong(p, byteOrder()); + byte* pData = p; +@@ -1536,7 +1541,9 @@ namespace Exiv2 { + } + } + Value::AutoPtr v = Value::create(typeId); +- assert(v.get()); ++ if (!v.get()) { ++ throw Error(58); ++ } + if ( !isize ) { + v->read(pData, size, byteOrder()); + } else { diff --git a/exiv2-doxygen.patch b/exiv2-doxygen.patch new file mode 100644 index 0000000..9508b97 --- /dev/null +++ b/exiv2-doxygen.patch @@ -0,0 +1,21 @@ +diff -up exiv2-trunk/config/Doxyfile.doxygen exiv2-trunk/config/Doxyfile +--- exiv2-trunk/config/Doxyfile.doxygen 2017-05-02 09:17:33.631909015 -0500 ++++ exiv2-trunk/config/Doxyfile 2017-05-02 09:18:03.019200824 -0500 +@@ -1688,7 +1688,7 @@ HIDE_UNDOC_RELATIONS = YES + # toolkit from AT&T and Lucent Bell Labs. The other options in this section + # have no effect if this option is set to NO (the default) + +-HAVE_DOT = YES ++#HAVE_DOT = YES + + # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is + # allowed to run in parallel. When set to 0 (the default) doxygen will +@@ -1705,7 +1705,7 @@ DOT_NUM_THREADS = 0 + # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the + # directory containing the font. + +-DOT_FONTNAME = Arial ++#DOT_FONTNAME = Arial + + # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. + # The default size is 10pt. diff --git a/exiv2-fix-documentation-build.patch b/exiv2-fix-documentation-build.patch new file mode 100644 index 0000000..b7ec106 --- /dev/null +++ b/exiv2-fix-documentation-build.patch @@ -0,0 +1,84 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7034bb6..27e90de 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -217,13 +217,16 @@ ADD_CUSTOM_TARGET(geotag-test COMMAND env EXIV2_BINDIR="${CMAKE_BINARY_DIR}"/bin + # effectively does a make doc on the root directory + # has to run 'make config' and './configure' + # and copy bin/taglist to /bin/taglist for use by 'make doc' +-IF( MINGW OR UNIX OR APPLE) +- ADD_CUSTOM_TARGET(doc +- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/doc" +- COMMAND chmod +x ./cmake_doc.sh +- COMMAND ./cmake_doc.sh "${CMAKE_BINARY_DIR}" +- ) +-ENDIF() ++# IF( MINGW OR UNIX OR APPLE) ++# ADD_CUSTOM_TARGET(doc ++# WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/doc" ++# COMMAND chmod +x ./cmake_doc.sh ++# COMMAND ./cmake_doc.sh "${CMAKE_BINARY_DIR}" ++# ) ++# ENDIF() ++ ++include(config/generateDoc.cmake REQUIRED) ++generate_documentation("${PROJECT_SOURCE_DIR}/config/Doxyfile") + + # That's all Folks! + ## +diff --git a/config/Doxyfile b/config/Doxyfile +index db62e1d..5d357a7 100644 +--- a/config/Doxyfile ++++ b/config/Doxyfile +@@ -52,7 +52,7 @@ PROJECT_LOGO = + # If a relative path is entered, it will be relative to the location + # where doxygen was started. If left blank the current directory will be used. + +-OUTPUT_DIRECTORY = ++OUTPUT_DIRECTORY = doc + + # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create + # 4096 sub-directories (in 2 levels) under the output directory of each output +@@ -1637,7 +1637,7 @@ TAGFILES = + # When a file name is specified after GENERATE_TAGFILE, doxygen will create + # a tag file that is based on the input files it reads. + +-GENERATE_TAGFILE = html/exiv2.xml ++GENERATE_TAGFILE = doc/html/exiv2.xml + + # If the ALLEXTERNALS tag is set to YES all external classes will be listed + # in the class index. If set to NO only the inherited external classes +diff --git a/config/generateDoc.cmake b/config/generateDoc.cmake +new file mode 100644 +index 0000000..2b66078 +--- /dev/null ++++ b/config/generateDoc.cmake +@@ -0,0 +1,28 @@ ++# -helper macro to add a "doc" target with CMake build system. ++# and configure doxy.config.in to doxy.config ++# ++# target "doc" allows building the documentation with doxygen/dot on WIN32, Linux and Mac ++# ++ ++find_package(Doxygen REQUIRED) ++ ++macro(generate_documentation DOX_CONFIG_FILE) ++ if(NOT EXISTS "${DOX_CONFIG_FILE}") ++ message(FATAL_ERROR "Configuration file for doxygen not found") ++ endif() ++ ++ #Define variables ++ set(INCDIR "${PROJECT_SOURCE_DIR}/include/exiv2") ++ set(SRCDIR "${PROJECT_SOURCE_DIR}/src") ++ set(ROOTDIR "${PROJECT_SOURCE_DIR}") ++ #set(TESTSDIR "${PROJECT_SOURCE_DIR}/tests") ++ ++ configure_file(${DOX_CONFIG_FILE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config @ONLY) #OUT-OF-PLACE LOCATION ++ set(DOXY_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/doxy.config") ++ add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${DOXY_CONFIG}) ++ ++ install(DIRECTORY "${PROJECT_BINARY_DIR}/doc/html/" DESTINATION "share/doc/html/lib${PROJECT_NAME}") ++ ++ set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES doc) ++endmacro() ++ diff --git a/exiv2-simplify-compiler-info-in-cmake.patch b/exiv2-simplify-compiler-info-in-cmake.patch new file mode 100644 index 0000000..36c2d91 --- /dev/null +++ b/exiv2-simplify-compiler-info-in-cmake.patch @@ -0,0 +1,43 @@ +From f9e3c712fe23a9cb661c998fc4fd14e7e5d641f5 Mon Sep 17 00:00:00 2001 +From: Luis Diaz Mas +Date: Thu, 17 Aug 2017 22:40:50 +0200 +Subject: Simplify compiler info handling in CMake + +(cherry picked from commit 69fb40fdc6d5797d10a025b9f5123978dda3bfa4) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index f2103c44..e49fb78b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -67,8 +67,8 @@ ENDIF() + # set include path for FindXXX.cmake files + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/config/") + +-IF( MINGW OR UNIX ) +- IF ( CMAKE_CXX_COMPILER STREQUAL "g++" OR CMAKE_C_COMPILER STREQUAL "gcc" ) ++if( MINGW OR UNIX ) ++ if (${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + ADD_DEFINITIONS(-Wall + -Wcast-align + -Wpointer-arith +@@ -79,18 +79,8 @@ IF( MINGW OR UNIX ) + ) + ENDIF() + +- execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE COMPILER_VERSION) +- string(REGEX MATCHALL "[a-z\+]+" GCC_COMPILER_COMPONENTS ${COMPILER_VERSION}) +- list(GET GCC_COMPILER_COMPONENTS 0 COMPILER) +- +- execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) +- string(REGEX MATCHALL "[0-9]+" GCC_VERSION_COMPONENTS ${GCC_VERSION}) +- list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) +- list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR) +- +- message(STATUS Compiler: ${COMPILER} " Major:" ${GCC_MAJOR} " Minor:" ${GCC_MINOR}) +- +- IF ( CYGWIN OR ( ${GCC_MAJOR} GREATER 5 )) ++ message(STATUS "Compiler info: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER}) ; version: ${CMAKE_CXX_COMPILER_VERSION}") ++ IF ( CYGWIN OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)) + ADD_DEFINITIONS( -std=gnu++98 ) # to support snprintf + ELSE() + ADD_DEFINITIONS( -std=c++98 ) diff --git a/exiv2-wrong-brackets.patch b/exiv2-wrong-brackets.patch new file mode 100644 index 0000000..a2f05fb --- /dev/null +++ b/exiv2-wrong-brackets.patch @@ -0,0 +1,39 @@ +From 1e07c98dfcbd8ac10ee02088f08235f5e1700148 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= +Date: Wed, 27 Sep 2017 23:38:49 +0200 +Subject: Fixed wrong brackets: size*count + pad can overflow before the cast + +=> Should fix #76 (most of the work has been done by Robin Mills in + 6e3855aed7ba8bb4731fc4087ca7f9078b2f3d97) + +The problem with #76 is the contents of the 26th IFD, with the +following contents: +tag: 0x8649 +type: 0x1 +count: 0xffff ffff +offset: 0x4974 + +The issue is the size of count (uint32_t), as adding anything to it +causes an overflow. Especially the expression: +(size*count + pad+20) +results in an overflow and gives 20 as a result instead of +0x100000014, thus the condition in the if in the next line is false +and the program continues to run (until it crashes at io.read). + +To properly account for the overflow, the brackets have to be removed, +as then the result is saved in the correctly sized type and not cast +after being calculated in the smaller type. + +diff --git a/src/image.cpp b/src/image.cpp +index ec5b873e..199671b9 100644 +--- a/src/image.cpp ++++ b/src/image.cpp +@@ -401,7 +401,7 @@ namespace Exiv2 { + // if ( offset > io.size() ) offset = 0; // Denial of service? + + // #55 memory allocation crash test/data/POC8 +- long long allocate = (long long) (size*count + pad+20); ++ long long allocate = (long long) size*count + pad+20; + if ( allocate > (long long) io.size() ) { + throw Error(57); + } diff --git a/exiv2.spec b/exiv2.spec new file mode 100644 index 0000000..771173a --- /dev/null +++ b/exiv2.spec @@ -0,0 +1,115 @@ +Name: exiv2 +Version: 0.26 +Release: 14 +Summary: Exif, IPTC and XMP metadata and the ICC Profile +License: GPLv2+ +URL: http://www.exiv2.org/ +Source0: https://github.com/Exiv2/%{name}/archive/exiv2-%{version}.tar.gz + +Patch0: exiv2-simplify-compiler-info-in-cmake.patch +Patch1: exiv2-fix-documentation-build.patch +Patch2: 0006-1296-Fix-submitted.patch + +Patch10: exiv2-CVE-2017-17723.patch +Patch11: exiv2-wrong-brackets.patch +Patch12: exiv2-CVE-2017-11683.patch +Patch13: exiv2-CVE-2017-14860.patch +Patch14: exiv2-CVE-2017-14864-CVE-2017-14862-CVE-2017-14859.patch +Patch15: exiv2-CVE-2017-17725.patch +Patch16: exiv2-CVE-2017-17669.patch +Patch17: exiv2-additional-security-fixes.patch +Patch18: exiv2-CVE-2018-10958.patch +Patch19: exiv2-CVE-2018-10998.patch +Patch20: exiv2-CVE-2018-11531.patch +Patch21: exiv2-CVE-2018-12264-CVE-2018-12265.patch +Patch22: exiv2-CVE-2018-14046.patch +Patch23: exiv2-CVE-2018-5772.patch +Patch24: exiv2-CVE-2018-8976.patch +Patch25: exiv2-CVE-2018-8977.patch + +Patch100: exiv2-doxygen.patch + +Patch6000: CVE-2018-19107-CVE-2018-19108-1.patch +Patch6001: CVE-2018-19107-CVE-2018-19108-2.patch +Patch6002: CVE-2018-17229-CVE-2018-17230.patch +Patch6003: CVE-2018-17282.patch +Patch6004: CVE-2017-1000126.patch +Patch6005: CVE-2018-19607.patch +Patch6006: CVE-2018-18915.patch +Patch6007: CVE-2019-13110.patch +Patch6008: CVE-2019-13113.patch +Patch6009: CVE-2019-13114.patch +Patch6010: CVE-2019-14982.patch + +Provides: exiv2-libs +Obsoletes: exiv2-libs + +BuildRequires: cmake expat-devel gcc-c++ gettext pkgconfig pkgconfig(libcurl) pkgconfig(libssh) +BuildRequires: doxygen graphviz libxslt zlib-devel + +%description +Exiv2 is a Cross-platform C++ library and a command line utility to manage image metadata. +It provides fast and easy read and write access to the Exif, IPTC and XMP metadata and the +ICC Profile embedded within digital images in various formats. + +%package devel +Summary: This package contains libraries, header and development files for exiv2 +Requires: %{name} = %{version}-%{release} + +%description devel +Exiv2 is a Cross-platform C++ library and a command line utility to manage image metadata. +This package provides head file,libraries for exiv2. + +%package help +Summary: help documentation for exiv2 +BuildArch: noarch + +%description help +Man pages and other related documents. + +%prep +%autosetup -p1 + +%build +export CPPFLAGS="-DBanAllEntityUsage=1" + +%{cmake} -DEXIV2_ENABLE_BUILD_PO:BOOL=ON -DEXIV2_ENABLE_BUILD_SAMPLES:BOOL=OFF -DEXIV2_ENABLE_LIBXMP:BOOL=ON + +%make_build +make doc + +%install +%make_install +%find_lang exiv2 + +%check +export PKG_CONFIG_PATH=%{buildroot}%{_libdir}/pkgconfig +test "$(pkg-config --modversion exiv2)" = "%{version}" +test -x %{buildroot}%{_libdir}/libexiv2.so + +%files -f exiv2.lang +%defattr(-,root,root) +%doc doc/ChangeLog COPYING +%{_bindir}/exiv2 +%{_libdir}/libexiv2.so.26* +%exclude %{_libdir}/pkgconfig/exiv2.lsm +%exclude %{_libdir}/libxmp.a + +%files devel +%{_libdir}/pkgconfig/exiv2.pc +%{_libdir}/libexiv2.so +%{_includedir}/exiv2/ + +%files help +%{_mandir}/man1/exiv2*.1* +%{_datadir}/doc/html/ + +%changelog +* Wed Sep 25 2019 huzunhao - 0.26-12.14 +- Type:cves +- ID:CVE-2019-14982 +- SUG:NA +- DESC:fix CVE-2019-14982 + +* Sat Sep 21 2019 Yanjie Guan - 0.26-12.13 +- Package init for openEuler