diff mbox series

[meta-oe,kirkstone,5/5] exiv2: patch CVE-2021-37621

Message ID 20260228202429.2424513-5-skandigraun@gmail.com
State New
Headers show
Series [meta-oe,kirkstone,1/5] exiv2: patch CVE-2021-37615 and CVE-2021-37616 | expand

Commit Message

Gyorgy Sarvari Feb. 28, 2026, 8:24 p.m. UTC
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
diff mbox series

Patch

diff --git a/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-1.patch b/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-1.patch
new file mode 100644
index 0000000000..1ca9fd2b0d
--- /dev/null
+++ b/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-1.patch
@@ -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);
diff --git a/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-2.patch b/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-2.patch
new file mode 100644
index 0000000000..a4578aae17
--- /dev/null
+++ b/meta-oe/recipes-support/exiv2/exiv2/CVE-2021-37621-2.patch
@@ -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()    )
diff --git a/meta-oe/recipes-support/exiv2/exiv2_0.27.3.bb b/meta-oe/recipes-support/exiv2/exiv2_0.27.3.bb
index 4001f1b639..eecd02d78a 100644
--- a/meta-oe/recipes-support/exiv2/exiv2_0.27.3.bb
+++ b/meta-oe/recipes-support/exiv2/exiv2_0.27.3.bb
@@ -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"