https://github.com/Exiv2/exiv2/pull/120 https://github.com/Exiv2/exiv2/commit/2d957cc2be6e6e9e5548baacf1ee12b0ac81cc43 For completeness sake, the following issues were fixed: CVE-2017-14864, CVE-2017-14862 and CVE-2017-14859 CVE-2017-14860 CVE-2017-11337, CVE-2017-11338, CVE-2017-11339, CVE-2017-11340 CVE-2017-11553, CVE-2017-12955, CVE-2017-12956, CVE-2017-12957 CVE-2017-11683 CVE-2017-11592 CVE-2017-11591 https://github.com/Exiv2/exiv2/pull/79 https://github.com/D4N/exiv2/commit/6cf8d085a7ac67cd5b021dfff36795f50e249eb2 CVE-2017-11336 https://github.com/Exiv2/exiv2/issues/124 https://github.com/Exiv2/exiv2/issues/76 CVE-2017-14857 diff --git a/include/exiv2/value.hpp b/include/exiv2/value.hpp index 64a8ca7f..b61c0f44 100644 --- a/include/exiv2/value.hpp +++ b/include/exiv2/value.hpp @@ -44,6 +44,7 @@ #include <sstream> #include <memory> #include <cstring> +#include <climits> // ***************************************************************************** // namespace extensions @@ -1658,19 +1659,21 @@ namespace Exiv2 { ok_ = true; return static_cast<long>(value_[n]); } +// #55 crash when value_[n].first == LONG_MIN +#define LARGE_INT 1000000 // Specialization for rational template<> inline long ValueType<Rational>::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; } // Specialization for unsigned rational template<> inline long ValueType<URational>::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 95589cd2..f2e1518b 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 80378c19..e90a9c0a 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -106,6 +106,11 @@ 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") }, + { 58, N_("corrupted image metadata") }, + { 59, N_("Arithmetic operation overflow") }, }; } diff --git a/src/image.cpp b/src/image.cpp index 0d828045..ec5b873e 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 and #56 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/src/jp2image.cpp b/src/jp2image.cpp index 1892fd43..09d023e2 100644 --- a/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/src/tiffvisitor.cpp b/src/tiffvisitor.cpp index 74f8d078..4ab733d4 100644 --- a/src/tiffvisitor.cpp +++ b/src/tiffvisitor.cpp @@ -47,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$") #include <iostream> #include <iomanip> #include <cassert> +#include <limits> // ***************************************************************************** namespace { @@ -1294,11 +1295,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; } @@ -1493,6 +1495,10 @@ namespace Exiv2 { } p += 4; uint32_t isize= 0; // size of Exif.Sony1.PreviewImage + + if (count > std::numeric_limits<uint32_t>::max() / typeSize) { + throw Error(59); + } uint32_t size = typeSize * count; uint32_t offset = getLong(p, byteOrder()); byte* pData = p; @@ -1516,7 +1522,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<uintptr_t>(baseOffset()) > std::numeric_limits<uintptr_t>::max() - static_cast<uintptr_t>(offset)) + || (static_cast<uintptr_t>(baseOffset() + offset) > std::numeric_limits<uintptr_t>::max() - reinterpret_cast<uintptr_t>(pData_))) + { + throw Error(59); + } + if (pData_ + static_cast<uintptr_t>(baseOffset()) + static_cast<uintptr_t>(offset) > pLast_) { + throw Error(58); + } pData = const_cast<byte*>(pData_) + baseOffset() + offset; + + // check for size being invalid if (size > static_cast<uint32_t>(pLast_ - pData)) { #ifndef SUPPRESS_WARNINGS EXV_ERROR << "Upper boundary of data for " @@ -1536,7 +1554,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/test/bugfixes-test.sh b/test/bugfixes-test.sh index f91c6759..c90ae559 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,13 +623,119 @@ 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 copyTestFile $filename 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 + + num=g57 + printf "$num " >&3 + filename=POC + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g79 + printf "$num " >&3 + filename=POC2 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g52 + printf "$num " >&3 + filename=POC5 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g51 + printf "$num " >&3 + filename=POC4 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g50 + printf "$num " >&3 + filename=POC3 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g53 + printf "$num " >&3 + filename=POC6 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g54 + printf "$num " >&3 + filename=POC9 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g58 + printf "$num " >&3 + filename=POC11 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g59 + printf "$num " >&3 + filename=POC12 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g60 + printf "$num " >&3 + filename=POC13 + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g71 + printf "$num " >&3 + filename=003-heap-buffer-over + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g73 + printf "$num " >&3 + filename=02-Invalid-mem-def + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g74 + printf "$num " >&3 + filename=005-invalid-mem + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + + num=g75 + printf "$num " >&3 + filename=008-invalid-mem + echo '------>' Bug $filename '<-------' >&2 + copyTestFile $filename + runTest exiv2 $filename + ) 3>&1 > $results 2>&1 printf "\n"