exiv2: patch CVE-2021-37621

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>
This commit is contained in:
Gyorgy Sarvari 2026-02-28 21:24:29 +01:00
parent 4f2440f62f
commit 08fe7e8f61
3 changed files with 214 additions and 0 deletions

View File

@ -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);

View File

@ -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() )

View File

@ -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"