new file mode 100644
@@ -0,0 +1,25 @@
+From 4fbd3390829f8418e1ec95252c2fd6b851850508 Mon Sep 17 00:00:00 2001
+From: Kevin Backhouse <kevinbackhouse@github.com>
+Date: Tue, 13 Jul 2021 22:50:16 +0100
+Subject: [PATCH] dirLength == 0 can cause an infinite loop.
+
+CVE: CVE-2021-37621
+Upstream-Status: Backport [https://github.com/Exiv2/exiv2/commit/191cd2690608f19335d82ed2be36c7ce8bdc60b9]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ src/image.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/image.cpp b/src/image.cpp
+index 2fa41e5..7c2eaa9 100644
+--- a/src/image.cpp
++++ b/src/image.cpp
+@@ -353,6 +353,8 @@ namespace Exiv2 {
+ throw Error(kerCorruptedMetadata);
+ }
+ uint16_t dirLength = byteSwap2(dir,0,bSwap);
++ // Prevent infinite loops. (GHSA-m479-7frc-gqqg)
++ enforce(dirLength > 0, kerCorruptedMetadata);
+
+ bool tooBig = dirLength > 500;
+ if ( tooBig ) throw Error(kerTiffDirectoryTooLarge);
new file mode 100644
@@ -0,0 +1,187 @@
+From 2973c3277af6922209a80985eccbd50b48088be6 Mon Sep 17 00:00:00 2001
+From: Kevin Backhouse <kevinbackhouse@github.com>
+Date: Tue, 13 Jul 2021 22:53:40 +0100
+Subject: [PATCH] Defensive programming in Image::printIFDStructure
+
+CVE: CVE-2021-37621
+Upstream-Status: Backport [https://github.com/Exiv2/exiv2/commit/d9fd4c4272df172ae89c0a9c41341adc75ebba86]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ src/image.cpp | 82 +++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 50 insertions(+), 32 deletions(-)
+
+diff --git a/src/image.cpp b/src/image.cpp
+index 7c2eaa9..6b1b1d8 100644
+--- a/src/image.cpp
++++ b/src/image.cpp
+@@ -27,6 +27,7 @@
+ #include "image.hpp"
+ #include "image_int.hpp"
+ #include "error.hpp"
++#include "enforce.hpp"
+ #include "futils.hpp"
+ #include "safe_op.hpp"
+ #include "slice.hpp"
+@@ -149,6 +150,19 @@ namespace {
+ // class member definitions
+ namespace Exiv2 {
+
++ // BasicIo::read() with error checking
++ static void readOrThrow(BasicIo& iIo, byte* buf, long rcount, ErrorCode err) {
++ const long nread = iIo.read(buf, rcount);
++ enforce(nread == rcount, err);
++ enforce(!iIo.error(), err);
++ }
++
++ // BasicIo::seek() with error checking
++ static void seekOrThrow(BasicIo& iIo, long offset, BasicIo::Position pos, ErrorCode err) {
++ const int r = iIo.seek(offset, pos);
++ enforce(r == 0, err);
++ }
++
+ Image::Image(int imageType,
+ uint16_t supportedMetadata,
+ BasicIo::AutoPtr io)
+@@ -347,11 +361,8 @@ namespace Exiv2 {
+
+ do {
+ // Read top of directory
+- const int seekSuccess = !io.seek(start,BasicIo::beg);
+- const long bytesRead = io.read(dir.pData_, 2);
+- if (!seekSuccess || bytesRead == 0) {
+- throw Error(kerCorruptedMetadata);
+- }
++ seekOrThrow(io, start, BasicIo::beg, kerCorruptedMetadata);
++ readOrThrow(io, dir.pData_, 2, kerCorruptedMetadata);
+ uint16_t dirLength = byteSwap2(dir,0,bSwap);
+ // Prevent infinite loops. (GHSA-m479-7frc-gqqg)
+ enforce(dirLength > 0, kerCorruptedMetadata);
+@@ -378,7 +389,7 @@ namespace Exiv2 {
+ }
+ bFirst = false;
+
+- io.read(dir.pData_, 12);
++ readOrThrow(io, dir.pData_, 12, kerCorruptedMetadata);
+ uint16_t tag = byteSwap2(dir,0,bSwap);
+ uint16_t type = byteSwap2(dir,2,bSwap);
+ uint32_t count = byteSwap4(dir,4,bSwap);
+@@ -411,20 +422,27 @@ namespace Exiv2 {
+ // if ( offset > io.size() ) offset = 0; // Denial of service?
+
+ // #55 and #56 memory allocation crash test/data/POC8
+- long long allocate = (long long) size*count + pad+20;
+- if ( allocate > (long long) io.size() ) {
++ const uint64_t allocate64 = static_cast<uint64_t>(size) * count + pad + 20;
++ if ( allocate64 > io.size() ) {
+ throw Error(kerInvalidMalloc);
+ }
+- DataBuf buf((long)allocate); // allocate a buffer
++ // Overflow check
++ enforce(allocate64 <= static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()), kerCorruptedMetadata);
++ enforce(allocate64 <= static_cast<uint64_t>(std::numeric_limits<long>::max()), kerCorruptedMetadata);
++ const long allocate = static_cast<long>(allocate64);
++ DataBuf buf(allocate); // allocate a buffer
+ std::memset(buf.pData_, 0, buf.size_);
+ std::memcpy(buf.pData_,dir.pData_+8,4); // copy dir[8:11] into buffer (short strings)
+- const bool bOffsetIsPointer = count*size > 4;
++
++ // We have already checked that this multiplication cannot overflow.
++ const uint32_t count_x_size = count*size;
++ const bool bOffsetIsPointer = count_x_size > 4;
+
+ if ( bOffsetIsPointer ) { // read into buffer
+- size_t restore = io.tell(); // save
+- io.seek(offset,BasicIo::beg); // position
+- io.read(buf.pData_,count*size);// read
+- io.seek(restore,BasicIo::beg); // restore
++ const long restore = io.tell(); // save
++ seekOrThrow(io, offset, BasicIo::beg, kerCorruptedMetadata); // position
++ readOrThrow(io, buf.pData_, static_cast<long>(count_x_size), kerCorruptedMetadata); // read
++ seekOrThrow(io, restore, BasicIo::beg, kerCorruptedMetadata); // restore
+ }
+
+ if ( bPrint ) {
+@@ -463,10 +481,10 @@ namespace Exiv2 {
+
+ if ( option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a/*SubIFDs*/ || type == tiffIfd) ) {
+ for ( size_t k = 0 ; k < count ; k++ ) {
+- size_t restore = io.tell();
++ const long restore = io.tell();
+ uint32_t offset = byteSwap4(buf,k*size,bSwap);
+ printIFDStructure(io,out,option,offset,bSwap,c,depth);
+- io.seek(restore,BasicIo::beg);
++ seekOrThrow(io, restore, BasicIo::beg, kerCorruptedMetadata);
+ }
+ } else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ ) {
+
+@@ -474,38 +492,38 @@ namespace Exiv2 {
+ throw Error(kerCorruptedMetadata);
+ }
+
+- const size_t restore = io.tell();
+- io.seek(offset, BasicIo::beg); // position
++ const long restore = io.tell();
++ seekOrThrow(io, offset, BasicIo::beg, kerCorruptedMetadata); // position
+ std::vector<byte> bytes(count) ; // allocate memory
+ // TODO: once we have C++11 use bytes.data()
+- const long read_bytes = io.read(&bytes[0], count);
+- io.seek(restore, BasicIo::beg);
++ readOrThrow(io, &bytes[0], count, kerCorruptedMetadata);
++ seekOrThrow(io, restore, BasicIo::beg, kerCorruptedMetadata);
+ // TODO: once we have C++11 use bytes.data()
+- IptcData::printStructure(out, makeSliceUntil(&bytes[0], read_bytes), depth);
++ IptcData::printStructure(out, makeSliceUntil(&bytes[0], count), depth);
+
+ } else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) {
+- size_t restore = io.tell(); // save
++ const long restore = io.tell(); // save
+
+ uint32_t jump= 10 ;
+ byte bytes[20] ;
+ const char* chars = (const char*) &bytes[0] ;
+- io.seek(offset,BasicIo::beg); // position
+- io.read(bytes,jump ) ; // read
++ seekOrThrow(io, offset, BasicIo::beg, kerCorruptedMetadata); // position
++ readOrThrow(io, bytes, jump, kerCorruptedMetadata) ; // read
+ bytes[jump]=0 ;
+ if ( ::strcmp("Nikon",chars) == 0 ) {
+ // tag is an embedded tiff
+- byte* bytes=new byte[count-jump] ; // allocate memory
+- io.read(bytes,count-jump) ; // read
+- MemIo memIo(bytes,count-jump) ; // create a file
++ const long byteslen = count-jump;
++ DataBuf bytes(byteslen); // allocate a buffer
++ readOrThrow(io, bytes.pData_, byteslen, kerCorruptedMetadata); // read
++ MemIo memIo(bytes.pData_, byteslen) ; // create a file
+ printTiffStructure(memIo,out,option,depth);
+- delete[] bytes ; // free
+ } else {
+ // tag is an IFD
+- io.seek(0,BasicIo::beg); // position
++ seekOrThrow(io, 0, BasicIo::beg, kerCorruptedMetadata); // position
+ printIFDStructure(io,out,option,offset,bSwap,c,depth);
+ }
+
+- io.seek(restore,BasicIo::beg); // restore
++ seekOrThrow(io, restore, BasicIo::beg, kerCorruptedMetadata); // restore
+ }
+ }
+
+@@ -518,7 +536,7 @@ namespace Exiv2 {
+ }
+ }
+ if ( start ) {
+- io.read(dir.pData_, 4);
++ readOrThrow(io, dir.pData_, 4, kerCorruptedMetadata);
+ start = tooBig ? 0 : byteSwap4(dir,0,bSwap);
+ }
+ } while (start) ;
+@@ -538,7 +556,7 @@ namespace Exiv2 {
+ DataBuf dir(dirSize);
+
+ // read header (we already know for certain that we have a Tiff file)
+- io.read(dir.pData_, 8);
++ readOrThrow(io, dir.pData_, 8, kerCorruptedMetadata);
+ char c = (char) dir.pData_[0] ;
+ bool bSwap = ( c == 'M' && isLittleEndianPlatform() )
+ || ( c == 'I' && isBigEndianPlatform() )
@@ -28,6 +28,8 @@ SRC_URI = "https://github.com/Exiv2/${BPN}/releases/download/v${PV}/${BP}-Source
file://CVE-2021-37619.patch \
file://CVE-2021-37620-1.patch \
file://CVE-2021-37620-2.patch \
+ file://CVE-2021-37621-1.patch \
+ file://CVE-2021-37621-2.patch \
"
SRC_URI[sha256sum] = "a79f5613812aa21755d578a297874fb59a85101e793edc64ec2c6bd994e3e778"
Details: https://nvd.nist.gov/vuln/detail/CVE-2021-37621 Backport the patch that is referenced by the NVD advisory. The regression test contains a binary patch, that couldn't be applied in the do_patch task. Due to this the test was not backported. It was however applied manually and executed successfully during the preparation of this patch. Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> --- .../exiv2/exiv2/CVE-2021-37621-1.patch | 25 +++ .../exiv2/exiv2/CVE-2021-37621-2.patch | 187 ++++++++++++++++++ meta-oe/recipes-support/exiv2/exiv2_0.27.3.bb | 2 + 3 files changed, 214 insertions(+) create mode 100644 meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-1.patch create mode 100644 meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-2.patch