From 99c067b851d9dc4ae6e91842991b93df010f0fe9 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Tue, 25 May 2021 12:12:53 +0200 Subject: [PATCH] CVE-2021-29623 exiv2: a read of uninitialized memory may lead to information leak Resolves: bz#1964183 CVE-2021-32617 exiv2: DoS due to quadratic complexity in ProcessUTF8Portion Resolves: bz#1964189 --- exiv2-CVE-2021-29623.patch | 26 ++++++++ exiv2-CVE-2021-32617.patch | 125 +++++++++++++++++++++++++++++++++++++ exiv2.spec | 11 +++- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 exiv2-CVE-2021-29623.patch create mode 100644 exiv2-CVE-2021-32617.patch diff --git a/exiv2-CVE-2021-29623.patch b/exiv2-CVE-2021-29623.patch new file mode 100644 index 0000000..120d5f7 --- /dev/null +++ b/exiv2-CVE-2021-29623.patch @@ -0,0 +1,26 @@ +From 82e46b5524fb904e6660dadd2c6d8e5e47375a1a Mon Sep 17 00:00:00 2001 +From: Kevin Backhouse +Date: Tue, 11 May 2021 12:14:33 +0100 +Subject: [PATCH] Use readOrThrow to check error conditions of iIo.read(). + +--- + src/webpimage.cpp | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/webpimage.cpp b/src/webpimage.cpp +index 7c64ff3d7..ca26e514a 100644 +--- a/src/webpimage.cpp ++++ b/src/webpimage.cpp +@@ -754,9 +754,9 @@ namespace Exiv2 { + byte webp[len]; + byte data[len]; + byte riff[len]; +- iIo.read(riff, len); +- iIo.read(data, len); +- iIo.read(webp, len); ++ readOrThrow(iIo, riff, len, Exiv2::kerCorruptedMetadata); ++ readOrThrow(iIo, data, len, Exiv2::kerCorruptedMetadata); ++ readOrThrow(iIo, webp, len, Exiv2::kerCorruptedMetadata); + bool matched_riff = (memcmp(riff, RiffImageId, len) == 0); + bool matched_webp = (memcmp(webp, WebPImageId, len) == 0); + iIo.seek(-12, BasicIo::cur); diff --git a/exiv2-CVE-2021-32617.patch b/exiv2-CVE-2021-32617.patch new file mode 100644 index 0000000..8ec91b7 --- /dev/null +++ b/exiv2-CVE-2021-32617.patch @@ -0,0 +1,125 @@ +From c261fbaa2567687eec6a595d3016212fd6ae648d Mon Sep 17 00:00:00 2001 +From: Kevin Backhouse +Date: Sun, 16 May 2021 15:05:08 +0100 +Subject: [PATCH] Fix quadratic complexity performance bug. + +--- + xmpsdk/src/XMPMeta-Parse.cpp | 57 +++++++++++++++++++++++------------- + 1 file changed, 36 insertions(+), 21 deletions(-) + +diff --git a/xmpsdk/src/XMPMeta-Parse.cpp b/xmpsdk/src/XMPMeta-Parse.cpp +index 9f66fe8..f9c37d7 100644 +--- a/xmpsdk/src/XMPMeta-Parse.cpp ++++ b/xmpsdk/src/XMPMeta-Parse.cpp +@@ -976,12 +976,26 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + { + const XMP_Uns8 * bufEnd = buffer + length; + +- const XMP_Uns8 * spanStart = buffer; + const XMP_Uns8 * spanEnd; +- +- for ( spanEnd = spanStart; spanEnd < bufEnd; ++spanEnd ) { + +- if ( (0x20 <= *spanEnd) && (*spanEnd <= 0x7E) && (*spanEnd != '&') ) continue; // A regular ASCII character. ++ // `buffer` is copied into this std::string. If `buffer` only ++ // contains valid UTF-8 and no escape characters, then the copy ++ // will be identical to the original, but invalid characters are ++ // replaced - usually with a space character. This std::string was ++ // added as a performance fix for: ++ // https://github.com/Exiv2/exiv2/security/advisories/GHSA-w8mv-g8qq-36mj ++ // Previously, the code was repeatedly calling ++ // `xmlParser->ParseBuffer()`, which turned out to have quadratic ++ // complexity, because expat kept reparsing the entire string from ++ // the beginning. ++ std::string copy; ++ ++ for ( spanEnd = buffer; spanEnd < bufEnd; ++spanEnd ) { ++ ++ if ( (0x20 <= *spanEnd) && (*spanEnd <= 0x7E) && (*spanEnd != '&') ) { ++ copy.push_back(*spanEnd); ++ continue; // A regular ASCII character. ++ } + + if ( *spanEnd >= 0x80 ) { + +@@ -992,21 +1006,20 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + if ( uniLen > 0 ) { + + // A valid UTF-8 character, keep it as-is. ++ copy.append((const char*)spanEnd, uniLen); + spanEnd += uniLen - 1; // ! The loop increment will put back the +1. + + } else if ( (uniLen < 0) && (! last) ) { + + // Have a partial UTF-8 character at the end of the buffer and more input coming. +- xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); ++ xmlParser->ParseBuffer ( copy.c_str(), copy.size(), false ); + return (spanEnd - buffer); + + } else { + + // Not a valid UTF-8 sequence. Replace the first byte with the Latin-1 equivalent. +- xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + const char * replacement = kReplaceLatin1 [ *spanEnd - 0x80 ]; +- xmlParser->ParseBuffer ( replacement, strlen ( replacement ), false ); +- spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart". ++ copy.append ( replacement ); + + } + +@@ -1014,11 +1027,12 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + + // Replace ASCII controls other than tab, LF, and CR with a space. + +- if ( (*spanEnd == kTab) || (*spanEnd == kLF) || (*spanEnd == kCR) ) continue; ++ if ( (*spanEnd == kTab) || (*spanEnd == kLF) || (*spanEnd == kCR) ) { ++ copy.push_back(*spanEnd); ++ continue; ++ } + +- xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); +- xmlParser->ParseBuffer ( " ", 1, false ); +- spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart". ++ copy.push_back(' '); + + } else { + +@@ -1030,18 +1044,21 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + if ( escLen < 0 ) { + + // Have a partial numeric escape in this buffer, wait for more input. +- if ( last ) continue; // No more buffers, not an escape, absorb as normal input. +- xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); ++ if ( last ) { ++ copy.push_back('&'); ++ continue; // No more buffers, not an escape, absorb as normal input. ++ } ++ xmlParser->ParseBuffer ( copy.c_str(), copy.size(), false ); + return (spanEnd - buffer); + + } else if ( escLen > 0 ) { + + // Have a complete numeric escape to replace. +- xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); +- xmlParser->ParseBuffer ( " ", 1, false ); +- spanStart = spanEnd + escLen; +- spanEnd = spanStart - 1; // ! The loop continuation will increment spanEnd! ++ copy.push_back(' '); ++ spanEnd = spanEnd + escLen - 1; // ! The loop continuation will increment spanEnd! + ++ } else { ++ copy.push_back('&'); + } + + } +@@ -1050,8 +1067,8 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + + XMP_Assert ( spanEnd == bufEnd ); + +- if ( spanStart < bufEnd ) xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); +- if ( last ) xmlParser->ParseBuffer ( " ", 1, true ); ++ copy.push_back(' '); ++ xmlParser->ParseBuffer ( copy.c_str(), copy.size(), true ); + + return length; + diff --git a/exiv2.spec b/exiv2.spec index d9537df..2ea059e 100644 --- a/exiv2.spec +++ b/exiv2.spec @@ -5,7 +5,7 @@ Summary: Exif and Iptc metadata manipulation library Name: exiv2 Version: 0.27.3 %global internal_ver %{version} -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ URL: http://www.exiv2.org/ @@ -23,6 +23,8 @@ Patch51: exiv2-CVE-2021-29457.patch Patch52: exiv2-CVE-2021-29458.patch Patch53: exiv2-CVE-2021-29470.patch Patch54: exiv2-CVE-2021-29473.patch +Patch55: exiv2-CVE-2021-29623.patch +Patch56: exiv2-CVE-2021-32617.patch ## upstreamable patches # don't unconditionally use -fcf-protection flag, not supported on all archs @@ -142,6 +144,13 @@ test -x %{buildroot}%{_libdir}/libexiv2.so %changelog +* Tue May 25 2021 Jan Grulich - 0.27.3-8 +- CVE-2021-29623 exiv2: a read of uninitialized memory may lead to information leak + Resolves: bz#1964183 + +- CVE-2021-32617 exiv2: DoS due to quadratic complexity in ProcessUTF8Portion + Resolves: bz#1964189 + * Mon May 03 2021 Jan Grulich - 0.27.3-7 - CVE-2021-3482: Fix heap-based buffer overflow in Jp2Image::readMetadata() CVE-2021-29458 exiv2: out-of-bounds read in Exiv2::Internal::CrwMap::encode