diff -Nru openexr-2.3.0/debian/changelog openexr-2.3.0/debian/changelog --- openexr-2.3.0/debian/changelog 2020-12-11 13:20:43.000000000 +0000 +++ openexr-2.3.0/debian/changelog 2021-04-01 12:47:09.000000000 +0000 @@ -1,3 +1,40 @@ +openexr (2.3.0-6ubuntu0.5) focal-security; urgency=medium + + * SECURITY UPDATE: shift overflow in FastHufDecoder + - debian/patches/CVE-2021-3474.patch: compute Huf codelengths using 64 + bit to prevent shift overflow in IlmImf/ImfFastHuf.cpp. + - CVE-2021-3474 + * SECURITY UPDATE: integer overflow in calculateNumTiles + - debian/patches/CVE-2021-3475.patch: compute level size with 64 bits + to avoid overflow in IlmImf/ImfTiledMisc.cpp. + - CVE-2021-3475 + * SECURITY UPDATE: shift overflows + - debian/patches/CVE-2021-3476.patch: ignore unused bits in B44 mode + detection in IlmImf/ImfB44Compressor.cpp. + - CVE-2021-3476 + * SECURITY UPDATE: out-of-bounds read via deep tile sample size + - debian/patches/CVE-2021-3477.patch: fix overflow computing deeptile + sample table size in IlmImf/ImfDeepTiledInputFile.cpp. + - CVE-2021-3477 + * SECURITY UPDATE: memory consumption via input file + - debian/patches/CVE-2021-3478-pre1.patch: reduce size limit for + scanline files; prevent large chunkoffset allocations in + IlmImf/ImfCompressor.cpp, IlmImf/ImfCompressor.h, IlmImf/ImfMisc.cpp, + IlmImf/ImfMultiPartInputFile.cpp, IlmImf/ImfScanLineInputFile.cpp. + - debian/patches/CVE-2021-3478.patch: sanity check ScanlineInput + bytesPerLine instead of lineOffset size in + IlmImf/ImfScanLineInputFile.cpp. + - CVE-2021-3478 + * SECURITY UPDATE: memory consumption in scanline API + - debian/patches/CVE-2021-3479-pre1.patch: address issues reported by + Undefined Behavior Sanitizer in IlmImf/ImfInputFile.cpp. + - debian/patches/CVE-2021-3479.patch: more efficient handling of filled + channels reading tiles with scanline API in IlmImf/ImfInputFile.cpp, + IlmImfTest/testScanLineApi.cpp. + - CVE-2021-3479 + + -- Marc Deslauriers Thu, 01 Apr 2021 08:47:09 -0400 + openexr (2.3.0-6ubuntu0.3) focal-security; urgency=medium * SECURITY UPDATE: DoS via heap overflow in chunkOffsetReconstruction diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3474.patch openexr-2.3.0/debian/patches/CVE-2021-3474.patch --- openexr-2.3.0/debian/patches/CVE-2021-3474.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3474.patch 2021-03-31 16:46:14.000000000 +0000 @@ -0,0 +1,35 @@ +Backport of: + +From c3ed4a1db1f39bf4524a644cb2af81dc8cfab33f Mon Sep 17 00:00:00 2001 +From: Peter Hillman +Date: Mon, 17 Aug 2020 12:29:03 +1200 +Subject: [PATCH] compute Huf codelengths using 64 bit to prevent shift + overflow + +Signed-off-by: Peter Hillman +--- + OpenEXR/IlmImf/ImfFastHuf.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/IlmImf/ImfFastHuf.cpp b/IlmImf/ImfFastHuf.cpp +index 9ccf2e15d..c04b56e75 100644 +--- a/IlmImf/ImfFastHuf.cpp ++++ b/IlmImf/ImfFastHuf.cpp +@@ -205,7 +205,7 @@ FastHufDecoder::FastHufDecoder + for (int l = _minCodeLength; l <= _maxCodeLength; ++l) + { + countTmp[l] = (double)codeCount[l] * +- (double)(2 << (_maxCodeLength-l)); ++ (double)(2ll << (_maxCodeLength-l)); + } + + for (int l = _minCodeLength; l <= _maxCodeLength; ++l) +@@ -215,7 +215,7 @@ FastHufDecoder::FastHufDecoder + for (int k =l + 1; k <= _maxCodeLength; ++k) + tmp += countTmp[k]; + +- tmp /= (double)(2 << (_maxCodeLength - l)); ++ tmp /= (double)(2ll << (_maxCodeLength - l)); + + base[l] = (Int64)ceil (tmp); + } diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3475.patch openexr-2.3.0/debian/patches/CVE-2021-3475.patch --- openexr-2.3.0/debian/patches/CVE-2021-3475.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3475.patch 2021-03-31 16:48:28.000000000 +0000 @@ -0,0 +1,31 @@ +Backport of: + +From 2a18ed424a854598c2a20b5dd7e782b436a1e753 Mon Sep 17 00:00:00 2001 +From: Cary Phillips +Date: Sun, 30 Aug 2020 16:15:10 -0700 +Subject: [PATCH] Avoid overflow in calculateNumTiles when size=MAX_INT (#825) + +* Avoid overflow in calculateNumTiles when size=MAX_INT + +Signed-off-by: Cary Phillips + +* Compute level size with 64 bits to avoid overflow + +Signed-off-by: Cary Phillips +--- + OpenEXR/IlmImf/ImfTiledMisc.cpp | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/IlmImf/ImfTiledMisc.cpp ++++ b/IlmImf/ImfTiledMisc.cpp +@@ -301,7 +301,9 @@ calculateNumTiles (int *numTiles, + { + for (int i = 0; i < numLevels; i++) + { +- numTiles[i] = (levelSize (min, max, i, rmode) + size - 1) / size; ++ // use 64 bits to avoid int overflow if size is large. ++ Int64 l = levelSize (min, max, i, rmode); ++ numTiles[i] = (l + size - 1) / size; + } + } + diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3476.patch openexr-2.3.0/debian/patches/CVE-2021-3476.patch --- openexr-2.3.0/debian/patches/CVE-2021-3476.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3476.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,70 @@ +Backport of: + +From eec0dba242bedd2778c973ae4af112107b33d9c9 Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Wed, 7 Oct 2020 09:47:48 +1300 +Subject: [PATCH] fix undefined behavior: ignore unused bits in B44 mode + detection (#832) + +Signed-off-by: Peter Hillman +--- + OpenEXR/IlmImf/ImfB44Compressor.cpp | 37 ++++++++++++++++------------- + 1 file changed, 20 insertions(+), 17 deletions(-) + +--- a/IlmImf/ImfB44Compressor.cpp ++++ b/IlmImf/ImfB44Compressor.cpp +@@ -381,26 +381,26 @@ unpack14 (const unsigned char b[14], uns + s[ 0] = (b[0] << 8) | b[1]; + + unsigned short shift = (b[ 2] >> 2); +- unsigned short bias = (0x20 << shift); ++ unsigned short bias = (0x20u << shift); + +- s[ 4] = s[ 0] + ((((b[ 2] << 4) | (b[ 3] >> 4)) & 0x3f) << shift) - bias; +- s[ 8] = s[ 4] + ((((b[ 3] << 2) | (b[ 4] >> 6)) & 0x3f) << shift) - bias; +- s[12] = s[ 8] + ((b[ 4] & 0x3f) << shift) - bias; ++ s[ 4] = s[ 0] + ((((b[ 2] << 4) | (b[ 3] >> 4)) & 0x3fu) << shift) - bias; ++ s[ 8] = s[ 4] + ((((b[ 3] << 2) | (b[ 4] >> 6)) & 0x3fu) << shift) - bias; ++ s[12] = s[ 8] + ((b[ 4] & 0x3fu) << shift) - bias; + +- s[ 1] = s[ 0] + ((b[ 5] >> 2) << shift) - bias; +- s[ 5] = s[ 4] + ((((b[ 5] << 4) | (b[ 6] >> 4)) & 0x3f) << shift) - bias; +- s[ 9] = s[ 8] + ((((b[ 6] << 2) | (b[ 7] >> 6)) & 0x3f) << shift) - bias; +- s[13] = s[12] + ((b[ 7] & 0x3f) << shift) - bias; ++ s[ 1] = s[ 0] + ((unsigned int) (b[ 5] >> 2) << shift) - bias; ++ s[ 5] = s[ 4] + ((((b[ 5] << 4) | (b[ 6] >> 4)) & 0x3fu) << shift) - bias; ++ s[ 9] = s[ 8] + ((((b[ 6] << 2) | (b[ 7] >> 6)) & 0x3fu) << shift) - bias; ++ s[13] = s[12] + ((b[ 7] & 0x3fu) << shift) - bias; + +- s[ 2] = s[ 1] + ((b[ 8] >> 2) << shift) - bias; +- s[ 6] = s[ 5] + ((((b[ 8] << 4) | (b[ 9] >> 4)) & 0x3f) << shift) - bias; +- s[10] = s[ 9] + ((((b[ 9] << 2) | (b[10] >> 6)) & 0x3f) << shift) - bias; +- s[14] = s[13] + ((b[10] & 0x3f) << shift) - bias; ++ s[ 2] = s[ 1] + ((unsigned int)(b[ 8] >> 2) << shift) - bias; ++ s[ 6] = s[ 5] + ((((b[ 8] << 4) | (b[ 9] >> 4)) & 0x3fu) << shift) - bias; ++ s[10] = s[ 9] + ((((b[ 9] << 2) | (b[10] >> 6)) & 0x3fu) << shift) - bias; ++ s[14] = s[13] + ((b[10] & 0x3fu) << shift) - bias; + +- s[ 3] = s[ 2] + ((b[11] >> 2) << shift) - bias; +- s[ 7] = s[ 6] + ((((b[11] << 4) | (b[12] >> 4)) & 0x3f) << shift) - bias; +- s[11] = s[10] + ((((b[12] << 2) | (b[13] >> 6)) & 0x3f) << shift) - bias; +- s[15] = s[14] + ((b[13] & 0x3f) << shift) - bias; ++ s[ 3] = s[ 2] + ((unsigned int)(b[11] >> 2) << shift) - bias; ++ s[ 7] = s[ 6] + ((((b[11] << 4) | (b[12] >> 4)) & 0x3fu) << shift) - bias; ++ s[11] = s[10] + ((((b[12] << 2) | (b[13] >> 6)) & 0x3fu) << shift) - bias; ++ s[15] = s[14] + ((b[13] & 0x3fu) << shift) - bias; + + for (int i = 0; i < 16; ++i) + { +@@ -949,7 +949,10 @@ B44Compressor::uncompress (const char *i + if (inSize < 3) + notEnoughData(); + +- if (((const unsigned char *)inPtr)[2] == 0xfc) ++ // ++ // If shift exponent is 63, call unpack14 (ignoring unused bits) ++ // ++ if (((const unsigned char *)inPtr)[2] >= (13<<2) ) + { + unpack3 ((const unsigned char *)inPtr, s); + inPtr += 3; diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3477.patch openexr-2.3.0/debian/patches/CVE-2021-3477.patch --- openexr-2.3.0/debian/patches/CVE-2021-3477.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3477.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,25 @@ +Backport of: + +From 467be80b75642efbbe6bdace558079f68c16acb1 Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Tue, 10 Nov 2020 08:35:59 +1300 +Subject: [PATCH] Fix overflow computing deeptile sample table size (#861) + +Signed-off-by: Peter Hillman +--- + src/lib/OpenEXR/ImfDeepTiledInputFile.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/IlmImf/ImfDeepTiledInputFile.cpp ++++ b/IlmImf/ImfDeepTiledInputFile.cpp +@@ -1025,8 +1025,8 @@ DeepTiledInputFile::initialize () + for (size_t i = 0; i < _data->tileBuffers.size(); i++) + _data->tileBuffers[i] = new TileBuffer (); + +- _data->maxSampleCountTableSize = _data->tileDesc.ySize * +- _data->tileDesc.xSize * ++ _data->maxSampleCountTableSize = static_cast(_data->tileDesc.ySize) * ++ static_cast(_data->tileDesc.xSize) * + sizeof(int); + + _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize); diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3478.patch openexr-2.3.0/debian/patches/CVE-2021-3478.patch --- openexr-2.3.0/debian/patches/CVE-2021-3478.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3478.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,35 @@ +Backport of: + +From bc88cdb6c97fbf5bc5d11ad8ca55306da931283a Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Fri, 20 Nov 2020 08:30:08 +1300 +Subject: [PATCH] sanity check ScanlineInput bytesPerLine instead of lineOffset + size (#863) + +Signed-off-by: Peter Hillman + +Co-authored-by: Cary Phillips +--- + src/lib/OpenEXR/ImfScanLineInputFile.cpp | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/IlmImf/ImfScanLineInputFile.cpp ++++ b/IlmImf/ImfScanLineInputFile.cpp +@@ -1120,14 +1120,14 @@ void ScanLineInputFile::initialize(const + _data->linesInBuffer) / _data->linesInBuffer; + + // +- // avoid allocating excessive memory due to large lineOffsets table size. ++ // avoid allocating excessive memory due to large lineOffsets and bytesPerLine table sizes. + // If the chunktablesize claims to be large, +- // check the file is big enough to contain the table before allocating memory ++ // check the file is big enough to contain the lineOffsets table before allocating memory + // in the bytesPerLineTable and the lineOffsets table. + // Attempt to read the last entry in the table. Either the seekg() or the read() + // call will throw an exception if the file is too small to contain the table + // +- if (lineOffsetSize > gLargeChunkTableSize) ++ if (lineOffsetSize * _data->linesInBuffer > gLargeChunkTableSize) + { + Int64 pos = _streamData->is->tellg(); + _streamData->is->seekg(pos + (lineOffsetSize-1)*sizeof(Int64)); diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3478-pre1.patch openexr-2.3.0/debian/patches/CVE-2021-3478-pre1.patch --- openexr-2.3.0/debian/patches/CVE-2021-3478-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3478-pre1.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,252 @@ +Backport of: + +From 0963ff1c4fcb3e748a9386685622747bfef00eb1 Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Fri, 28 Aug 2020 17:00:05 +1200 +Subject: [PATCH] reduce size limit for scanline files; prevent large + chunkoffset allocations (#824) + +* reduce size limit for scanline files; protect against large chunkoffset allocations + +Signed-off-by: Peter Hillman + +* bugfix for memory limit changes + +Signed-off-by: Peter Hillman + +* rearrange chunkoffset test to protect bytesperline table too + +Signed-off-by: Peter Hillman + +* remove extraneous function declaration; tidy comments + +Signed-off-by: Peter Hillman +--- + OpenEXR/IlmImf/ImfCompressor.cpp | 31 +++++++++++++++++ + OpenEXR/IlmImf/ImfCompressor.h | 8 +++++ + OpenEXR/IlmImf/ImfMisc.cpp | 33 +----------------- + OpenEXR/IlmImf/ImfMisc.h | 1 + + OpenEXR/IlmImf/ImfMultiPartInputFile.cpp | 24 ++++++++++++- + OpenEXR/IlmImf/ImfScanLineInputFile.cpp | 44 +++++++++++++++++++----- + 6 files changed, 99 insertions(+), 42 deletions(-) + +--- a/IlmImf/ImfCompressor.cpp ++++ b/IlmImf/ImfCompressor.cpp +@@ -176,6 +176,37 @@ newCompressor (Compression c, size_t max + } + + ++// for a given compression type, return the number of scanlines ++// compressed into a single chunk ++// TODO add to API and move to ImfCompressor.cpp ++int ++numLinesInBuffer(Compression comp) ++{ ++ switch(comp) ++ { ++ case NO_COMPRESSION : ++ case RLE_COMPRESSION: ++ case ZIPS_COMPRESSION: ++ return 1; ++ case ZIP_COMPRESSION: ++ return 16; ++ case PIZ_COMPRESSION: ++ return 32; ++ case PXR24_COMPRESSION: ++ return 16; ++ case B44_COMPRESSION: ++ case B44A_COMPRESSION: ++ case DWAA_COMPRESSION: ++ return 32; ++ case DWAB_COMPRESSION: ++ return 256; ++ ++ default: ++ throw IEX_NAMESPACE::ArgExc ("Unknown compression type"); ++ } ++} ++ ++ + Compressor * + newTileCompressor (Compression c, + size_t tileLineSize, +--- a/IlmImf/ImfCompressor.h ++++ b/IlmImf/ImfCompressor.h +@@ -268,6 +268,14 @@ Compressor * newTileCompressor (Compr + const Header &hdr); + + ++//----------------------------------------------------------------- ++// Return the maximum number of scanlines in each chunk ++// of a scanline image using the given compression scheme ++//----------------------------------------------------------------- ++ ++IMF_EXPORT ++int numLinesInBuffer(Compression comp); ++ + OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_EXIT + + #endif +--- a/IlmImf/ImfMisc.cpp ++++ b/IlmImf/ImfMisc.cpp +@@ -1827,38 +1827,7 @@ usesLongNames (const Header &header) + return false; + } + +-namespace +-{ +-// for a given compression type, return the number of scanlines +-// compressed into a single chunk +-// TODO add to API and move to ImfCompressor.cpp +-int +-numLinesInBuffer(Compression comp) +-{ +- switch(comp) +- { +- case NO_COMPRESSION : +- case RLE_COMPRESSION: +- case ZIPS_COMPRESSION: +- return 1; +- case ZIP_COMPRESSION: +- return 16; +- case PIZ_COMPRESSION: +- return 32; +- case PXR24_COMPRESSION: +- return 16; +- case B44_COMPRESSION: +- case B44A_COMPRESSION: +- case DWAA_COMPRESSION: +- return 32; +- case DWAB_COMPRESSION: +- return 256; + +- default: +- throw IEX_NAMESPACE::ArgExc ("Unknown compression type"); +- } +-} +-} + + int + getScanlineChunkOffsetTableSize(const Header& header) +--- a/IlmImf/ImfMultiPartInputFile.cpp ++++ b/IlmImf/ImfMultiPartInputFile.cpp +@@ -726,7 +726,9 @@ MultiPartInputFile::Data::getPart(int pa + return parts[partNumber]; + } + +- ++namespace{ ++static const int gLargeChunkTableSize = 1024*1024; ++} + + void + MultiPartInputFile::Data::readChunkOffsetTables(bool reconstructChunkOffsetTable) +@@ -736,8 +738,28 @@ MultiPartInputFile::Data::readChunkOffse + for (size_t i = 0; i < parts.size(); i++) + { + int chunkOffsetTableSize = getChunkOffsetTableSize(parts[i]->header); ++ ++ // ++ // avoid allocating excessive memory. ++ // If the chunktablesize claims to be large, ++ // check the file is big enough to contain the table before allocating memory. ++ // Attempt to read the last entry in the table. Either the seekg() or the read() ++ // call will throw an exception if the file is too small to contain the table ++ // ++ if (chunkOffsetTableSize > gLargeChunkTableSize) ++ { ++ Int64 pos = is->tellg(); ++ is->seekg(pos + (chunkOffsetTableSize-1)*sizeof(Int64)); ++ Int64 temp; ++ OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read (*is, temp); ++ is->seekg(pos); ++ ++ } ++ + parts[i]->chunkOffsets.resize(chunkOffsetTableSize); + ++ ++ + for (int j = 0; j < chunkOffsetTableSize; j++) + OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read (*is, parts[i]->chunkOffsets[j]); + +--- a/IlmImf/ImfScanLineInputFile.cpp ++++ b/IlmImf/ImfScanLineInputFile.cpp +@@ -1091,7 +1091,7 @@ newLineBufferTask (TaskGroup *group, + } + + +- ++static const int gLargeChunkTableSize = 1024*1024; + + } // namespace + +@@ -1111,25 +1111,53 @@ void ScanLineInputFile::initialize(const + _data->minY = dataWindow.min.y; + _data->maxY = dataWindow.max.y; + ++ Compression comp = _data->header.compression(); ++ ++ _data->linesInBuffer = ++ numLinesInBuffer (comp); ++ ++ int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y + ++ _data->linesInBuffer) / _data->linesInBuffer; ++ ++ // ++ // avoid allocating excessive memory due to large lineOffsets table size. ++ // If the chunktablesize claims to be large, ++ // check the file is big enough to contain the table before allocating memory ++ // in the bytesPerLineTable and the lineOffsets table. ++ // Attempt to read the last entry in the table. Either the seekg() or the read() ++ // call will throw an exception if the file is too small to contain the table ++ // ++ if (lineOffsetSize > gLargeChunkTableSize) ++ { ++ Int64 pos = _streamData->is->tellg(); ++ _streamData->is->seekg(pos + (lineOffsetSize-1)*sizeof(Int64)); ++ Int64 temp; ++ OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read (*_streamData->is, temp); ++ _streamData->is->seekg(pos); ++ ++ } ++ ++ + size_t maxBytesPerLine = bytesPerLineTable (_data->header, + _data->bytesPerLine); +- +- if(maxBytesPerLine > INT_MAX) ++ ++ if (maxBytesPerLine*numLinesInBuffer(comp) > INT_MAX) + { + throw IEX_NAMESPACE::InputExc("maximum bytes per scanline exceeds maximum permissible size"); + } + + ++ // ++ // allocate compressor objects ++ // + for (size_t i = 0; i < _data->lineBuffers.size(); i++) + { +- _data->lineBuffers[i] = new LineBuffer (newCompressor +- (_data->header.compression(), ++ _data->lineBuffers[i] = new LineBuffer (newCompressor(comp, + maxBytesPerLine, + _data->header)); + } + +- _data->linesInBuffer = +- numLinesInBuffer (_data->lineBuffers[0]->compressor); ++ + + _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer; + +@@ -1146,8 +1174,6 @@ void ScanLineInputFile::initialize(const + _data->linesInBuffer, + _data->offsetInLineBuffer); + +- int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y + +- _data->linesInBuffer) / _data->linesInBuffer; + + _data->lineOffsets.resize (lineOffsetSize); + } diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3479.patch openexr-2.3.0/debian/patches/CVE-2021-3479.patch --- openexr-2.3.0/debian/patches/CVE-2021-3479.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3479.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,546 @@ +Backport of: + +From d80f11f4f55100d007ae80a162bf257ec291612c Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Fri, 11 Sep 2020 11:02:20 +1200 +Subject: [PATCH] More efficient handling of filled channels reading tiles with + scanline API (#830) + +* refactor channel filling in InputFile API with tiled source + +Signed-off-by: Peter Hillman + +* handle edge-case of empty framebuffer + +Signed-off-by: Peter Hillman +--- + OpenEXR/IlmImf/ImfInputFile.cpp | 271 ++++++++++++++++--------- + OpenEXR/IlmImfTest/testScanLineApi.cpp | 134 +++++++++++- + 2 files changed, 310 insertions(+), 95 deletions(-) + +--- a/IlmImf/ImfInputFile.cpp ++++ b/IlmImf/ImfInputFile.cpp +@@ -272,9 +272,14 @@ bufferedReadPixels (InputFile::Data* ifd + // + // We don't have any valid buffered info, so we need to read in + // from the file. ++ // if no channels are being read that are present in file, cachedBuffer will be empty + // + +- ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j); ++ if (ifd->cachedBuffer->begin() != ifd->cachedBuffer->end()) ++ { ++ ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j); ++ } ++ + ifd->cachedTileY = j; + } + +@@ -283,58 +288,135 @@ bufferedReadPixels (InputFile::Data* ifd + // framebuffer. + // + +- for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin(); +- k != ifd->cachedBuffer->end(); ++ for (FrameBuffer::ConstIterator k = ifd->tFileBuffer.begin(); ++ k != ifd->tFileBuffer.end(); + ++k) + { +- Slice fromSlice = k.slice(); // slice to write from +- Slice toSlice = ifd->tFileBuffer[k.name()]; // slice to write to + +- char *fromPtr, *toPtr; +- int size = pixelTypeSize (toSlice.type); + +- int xStart = levelRange.min.x; +- int yStart = minYThisRow; ++ Slice toSlice = k.slice(); // slice to read from ++ char* toPtr; + +- while (modp (xStart, toSlice.xSampling) != 0) +- ++xStart; ++ int xStart = levelRange.min.x; ++ int yStart = minYThisRow; + +- while (modp (yStart, toSlice.ySampling) != 0) +- ++yStart; ++ while (modp (xStart, toSlice.xSampling) != 0) ++ ++xStart; + ++ while (modp (yStart, toSlice.ySampling) != 0) ++ ++yStart; + +- intptr_t fromBase = reinterpret_cast(fromSlice.base); ++ FrameBuffer::ConstIterator c = ifd->cachedBuffer->find(k.name()); + intptr_t toBase = reinterpret_cast(toSlice.base); + +- for (int y = yStart; +- y <= maxYThisRow; +- y += toSlice.ySampling) ++ ++ if( c!=ifd->cachedBuffer->end()) + { +- // +- // Set the pointers to the start of the y scanline in +- // this row of tiles +- // +- fromPtr = reinterpret_cast (fromBase + +- (y - tileRange.min.y) * fromSlice.yStride + +- xStart * fromSlice.xStride); +- +- toPtr = reinterpret_cast (toBase + +- divp (y, toSlice.ySampling) * toSlice.yStride + +- divp (xStart, toSlice.xSampling) * toSlice.xStride); +- +- // +- // Copy all pixels for the scanline in this row of tiles +- // +- +- for (int x = xStart; +- x <= levelRange.max.x; +- x += toSlice.xSampling) ++ // ++ // output channel was read from source image: copy to output slice ++ // ++ Slice fromSlice = c.slice(); // slice to write to ++ intptr_t fromBase = reinterpret_cast(fromSlice.base); ++ ++ int size = pixelTypeSize (toSlice.type); ++ char* fromPtr; ++ ++ for (int y = yStart; ++ y <= maxYThisRow; ++ y += toSlice.ySampling) + { +- for (int i = 0; i < size; ++i) +- toPtr[i] = fromPtr[i]; ++ // ++ // Set the pointers to the start of the y scanline in ++ // this row of tiles ++ // ++ ++ fromPtr = reinterpret_cast (fromBase + ++ (y - tileRange.min.y) * fromSlice.yStride + ++ xStart * fromSlice.xStride); ++ ++ toPtr = reinterpret_cast (toBase + ++ divp (y, toSlice.ySampling) * toSlice.yStride + ++ divp (xStart, toSlice.xSampling) * toSlice.xStride); ++ ++ // ++ // Copy all pixels for the scanline in this row of tiles ++ // ++ ++ for (int x = xStart; ++ x <= levelRange.max.x; ++ x += toSlice.xSampling) ++ { ++ for (int i = 0; i < size; ++i) ++ toPtr[i] = fromPtr[i]; ++ ++ fromPtr += fromSlice.xStride * toSlice.xSampling; ++ toPtr += toSlice.xStride; ++ } ++ } ++ } ++ else ++ { + +- fromPtr += fromSlice.xStride * toSlice.xSampling; +- toPtr += toSlice.xStride; ++ // ++ // channel wasn't present in source file: fill output slice ++ // ++ for (int y = yStart; ++ y <= maxYThisRow; ++ y += toSlice.ySampling) ++ { ++ ++ toPtr = reinterpret_cast (toBase+ ++ divp (y, toSlice.ySampling) * toSlice.yStride + ++ divp (xStart, toSlice.xSampling) * toSlice.xStride); ++ ++ // ++ // Copy all pixels for the scanline in this row of tiles ++ // ++ ++ switch ( toSlice.type) ++ { ++ case UINT: ++ { ++ unsigned int fill = toSlice.fillValue; ++ for (int x = xStart; ++ x <= levelRange.max.x; ++ x += toSlice.xSampling) ++ { ++ * reinterpret_cast(toPtr) = fill; ++ toPtr += toSlice.xStride; ++ } ++ break; ++ } ++ case HALF : ++ { ++ half fill = toSlice.fillValue; ++ for (int x = xStart; ++ x <= levelRange.max.x; ++ x += toSlice.xSampling) ++ { ++ * reinterpret_cast(toPtr) = fill; ++ toPtr += toSlice.xStride; ++ } ++ break; ++ } ++ case FLOAT : ++ { ++ float fill = toSlice.fillValue; ++ for (int x = xStart; ++ x <= levelRange.max.x; ++ x += toSlice.xSampling) ++ { ++ * reinterpret_cast(toPtr) = fill; ++ toPtr += toSlice.xStride; ++ } ++ break; ++ } ++ case NUM_PIXELTYPES : ++ { ++ break; ++ } ++ ++ } + } + } + } +@@ -691,60 +773,67 @@ InputFile::setFrameBuffer (const FrameBu + { + Slice s = k.slice(); + +- switch (s.type) +- { +- case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT: +- +- _data->cachedBuffer->insert +- (k.name(), +- Slice (UINT, +- (char *)(new unsigned int[tileRowSize] - +- _data->offset), +- sizeof (unsigned int), +- sizeof (unsigned int) * +- _data->tFile->levelWidth(0), +- 1, 1, +- s.fillValue, +- false, true)); +- break; +- +- case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF: +- +- _data->cachedBuffer->insert +- (k.name(), +- Slice (HALF, +- (char *)(new half[tileRowSize] - +- _data->offset), +- sizeof (half), +- sizeof (half) * +- _data->tFile->levelWidth(0), +- 1, 1, +- s.fillValue, +- false, true)); +- break; +- +- case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT: +- +- _data->cachedBuffer->insert +- (k.name(), +- Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, +- (char *)(new float[tileRowSize] - +- _data->offset), +- sizeof(float), +- sizeof(float) * +- _data->tFile->levelWidth(0), +- 1, 1, +- s.fillValue, +- false, true)); +- break; ++ // ++ // omit adding channels that are not listed - 'fill' channels are added later ++ // ++ if ( _data->header.channels().find(k.name()) != _data->header.channels().end() ) ++ { ++ switch (s.type) ++ { ++ case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT: ++ ++ _data->cachedBuffer->insert ++ (k.name(), ++ Slice (UINT, ++ (char *)(new unsigned int[tileRowSize] - ++ _data->offset), ++ sizeof (unsigned int), ++ sizeof (unsigned int) * ++ _data->tFile->levelWidth(0), ++ 1, 1, ++ s.fillValue, ++ false, true)); ++ break; ++ ++ case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF: ++ ++ _data->cachedBuffer->insert ++ (k.name(), ++ Slice (HALF, ++ (char *)(new half[tileRowSize] - ++ _data->offset), ++ sizeof (half), ++ sizeof (half) * ++ _data->tFile->levelWidth(0), ++ 1, 1, ++ s.fillValue, ++ false, true)); ++ break; ++ ++ case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT: ++ ++ _data->cachedBuffer->insert ++ (k.name(), ++ Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT, ++ (char *)(new float[tileRowSize] - ++ _data->offset), ++ sizeof(float), ++ sizeof(float) * ++ _data->tFile->levelWidth(0), ++ 1, 1, ++ s.fillValue, ++ false, true)); ++ break; + +- default: ++ default: + +- throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type."); +- } ++ throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type."); ++ } ++ } + } + + _data->tFile->setFrameBuffer (*_data->cachedBuffer); ++ + } + + _data->tFileBuffer = frameBuffer; +--- a/IlmImfTest/testScanLineApi.cpp ++++ b/IlmImfTest/testScanLineApi.cpp +@@ -90,7 +90,9 @@ writeRead (const Array2D & + int yOffset, + Compression comp, + LevelMode mode, +- LevelRoundingMode rmode) ++ LevelRoundingMode rmode, ++ bool fillChannel ++ ) + { + // + // Write the pixel data in pi1, ph1 and ph2 to a tiled +@@ -256,6 +258,16 @@ writeRead (const Array2D & + Array2D ph2 (h, w); + Array2D pf2 (h, w); + ++ Array2D fi2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D fh2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D ff2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ ++ ++ const unsigned int fillInt = 12; ++ const half fillHalf = 4.5; ++ const float fillFloat = M_PI; ++ ++ + FrameBuffer fb; + + fb.insert ("I", // name +@@ -279,6 +291,30 @@ writeRead (const Array2D & + sizeof (pf2[0][0]) * w) // yStride + ); + ++ if(fillChannel) ++ { ++ fb.insert ("FI", // name ++ Slice (IMF::UINT, // type ++ (char *) &fi2[-dwy][-dwx],// base ++ sizeof (fi2[0][0]), // xStride ++ sizeof (fi2[0][0]) * w,1,1,fillInt) // yStride ++ ); ++ ++ fb.insert ("FH", // name ++ Slice (IMF::HALF, // type ++ (char *) &fh2[-dwy][-dwx],// base ++ sizeof (fh2[0][0]), // xStride ++ sizeof (fh2[0][0]) * w,1,1,fillHalf) // yStride ++ ); ++ ++ fb.insert ("FF", // name ++ Slice (IMF::FLOAT, // type ++ (char *) &ff2[-dwy][-dwx],// base ++ sizeof (ff2[0][0]), // xStride ++ sizeof (ff2[0][0]) * w,1,1,fillFloat) // yStride ++ ); ++ } ++ + in.setFrameBuffer (fb); + for (int y = dw.min.y; y <= dw.max.y; ++y) + in.readPixels (y); +@@ -316,6 +352,13 @@ writeRead (const Array2D & + assert (pi1[y][x] == pi2[y][x]); + assert (ph1[y][x] == ph2[y][x]); + assert (pf1[y][x] == pf2[y][x]); ++ ++ if (fillChannel) ++ { ++ assert(fi2[y][x] == fillInt); ++ assert(fh2[y][x] == fillHalf); ++ assert(ff2[y][x] == fillFloat); ++ } + } + } + } +@@ -335,6 +378,10 @@ writeRead (const Array2D & + Array2D ph2 (h, w); + Array2D pf2 (h, w); + ++ Array2D fi2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D fh2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D ff2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ + FrameBuffer fb; + + fb.insert ("I", // name +@@ -357,6 +404,34 @@ writeRead (const Array2D & + sizeof (pf2[0][0]), // xStride + sizeof (pf2[0][0]) * w) // yStride + ); ++ const unsigned int fillInt = 21; ++ const half fillHalf = 42; ++ const float fillFloat = 2.8; ++ ++ if (fillChannel) ++ { ++ fb.insert ("FI", // name ++ Slice (IMF::UINT, // type ++ (char *) &fi2[-dwy][-dwx],// base ++ sizeof (fi2[0][0]), // xStride ++ sizeof (fi2[0][0]) * w,1,1,fillInt) // yStride ++ ); ++ ++ fb.insert ("FH", // name ++ Slice (IMF::HALF, // type ++ (char *) &fh2[-dwy][-dwx],// base ++ sizeof (fh2[0][0]), // xStride ++ sizeof (fh2[0][0]) * w,1,1,fillHalf) // yStride ++ ); ++ ++ fb.insert ("FF", // name ++ Slice (IMF::FLOAT, // type ++ (char *) &ff2[-dwy][-dwx],// base ++ sizeof (ff2[0][0]), // xStride ++ sizeof (ff2[0][0]) * w,1,1,fillFloat) // yStride ++ ); ++ ++ } + + in.setFrameBuffer (fb); + for (int y = dw.max.y; y >= dw.min.y; --y) +@@ -395,6 +470,12 @@ writeRead (const Array2D & + assert (pi1[y][x] == pi2[y][x]); + assert (ph1[y][x] == ph2[y][x]); + assert (pf1[y][x] == pf2[y][x]); ++ if (fillChannel) ++ { ++ assert(fi2[y][x] == fillInt); ++ assert(fh2[y][x] == fillHalf); ++ assert(ff2[y][x] == fillFloat); ++ } + } + } + } +@@ -415,6 +496,17 @@ writeRead (const Array2D & + Array2D ph2 (h, w); + Array2D pf2 (h, w); + ++ ++ Array2D fi2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D fh2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ Array2D ff2 (fillChannel ? h : 1 , fillChannel ? w : 1); ++ ++ ++ const unsigned int fillInt = 81; ++ const half fillHalf = 0.5; ++ const float fillFloat = 7.8; ++ ++ + for (int y = dw.min.y; y <= dw.max.y; ++y) + { + FrameBuffer fb; +@@ -440,6 +532,31 @@ writeRead (const Array2D & + 0) // yStride + ); + ++ if (fillChannel) ++ { ++ fb.insert ("FI", // name ++ Slice (IMF::UINT, // type ++ (char *) &fi2[y - dwy][-dwx], // base ++ sizeof (fi2[0][0]), // xStride ++ 0,1,1,fillInt) // yStride ++ ); ++ ++ fb.insert ("FH", // name ++ Slice (IMF::HALF, // type ++ (char *) &fh2[y - dwy][-dwx], // base ++ sizeof (fh2[0][0]), // xStride ++ 0,1,1,fillHalf) // yStride ++ ); ++ ++ fb.insert ("FF", // name ++ Slice (IMF::FLOAT, // type ++ (char *) &ff2[y - dwy][-dwx], // base ++ sizeof (ff2[0][0]), // xStride ++ 0,1,1,fillFloat) // yStride ++ ); ++ ++ } ++ + in.setFrameBuffer (fb); + in.readPixels (y); + } +@@ -477,7 +594,14 @@ writeRead (const Array2D & + assert (pi1[y][x] == pi2[y][x]); + assert (ph1[y][x] == ph2[y][x]); + assert (pf1[y][x] == pf2[y][x]); ++ if (fillChannel) ++ { ++ assert (fi2[y][x] == fillInt); ++ assert (fh2[y][x] == fillHalf); ++ assert (ff2[y][x] == fillFloat); ++ } + } ++ + } + } + +@@ -502,11 +626,13 @@ writeRead (const std::string &tempDir, + std::string filename = tempDir + "imf_test_scanline_api.exr"; + + writeRead (pi, ph, pf, filename.c_str(), lorder, W, H, +- xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode); ++ xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , false); ++ writeRead (pi, ph, pf, filename.c_str(), lorder, W, H, ++ xSize, ySize, dx, dy, comp, MIPMAP_LEVELS, rmode , false ); + writeRead (pi, ph, pf, filename.c_str(), lorder, W, H, +- xSize, ySize, dx, dy, comp, MIPMAP_LEVELS, rmode); ++ xSize, ySize, dx, dy, comp, RIPMAP_LEVELS, rmode , false); + writeRead (pi, ph, pf, filename.c_str(), lorder, W, H, +- xSize, ySize, dx, dy, comp, RIPMAP_LEVELS, rmode); ++ xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , true); + } + + } // namespace diff -Nru openexr-2.3.0/debian/patches/CVE-2021-3479-pre1.patch openexr-2.3.0/debian/patches/CVE-2021-3479-pre1.patch --- openexr-2.3.0/debian/patches/CVE-2021-3479-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ openexr-2.3.0/debian/patches/CVE-2021-3479-pre1.patch 2021-04-01 12:47:09.000000000 +0000 @@ -0,0 +1,87 @@ +Partial backport of: + +From 3c05eacda70e3ac00ca45287aa4c8de7262a0673 Mon Sep 17 00:00:00 2001 +From: peterhillman +Date: Tue, 8 Sep 2020 10:03:03 +1200 +Subject: [PATCH] Address issues reported by Undefined Behavior Sanitizer + running IlmImfTest (#828) + +* add join() to IlmThread for avoiding use-after-free race condition + +Signed-off-by: Peter Hillman + +* Use intptr_t in pointer math with negatives (pointer overflow behavior undefined) + +Signed-off-by: Peter Hillman + +* fix undefined behavior reading non-aligned 32/64 bit ints + +Signed-off-by: Peter Hillman + +* fix undefined behavior warnings in IlmImfTest + +Signed-off-by: Peter Hillman + +* cleaner pointer casting/typo fix + +Signed-off-by: Peter Hillman +--- + IlmBase/IlmThread/IlmThread.cpp | 12 ++++++++ + IlmBase/IlmThread/IlmThread.h | 5 ++++ + IlmBase/IlmThread/IlmThreadPool.cpp | 4 ++- + IlmBase/IlmThread/IlmThreadPosix.cpp | 7 +++++ + IlmBase/IlmThread/IlmThreadWin32.cpp | 8 +++++ + OpenEXR/IlmImf/ImfCompositeDeepScanLine.cpp | 9 +++--- + OpenEXR/IlmImf/ImfDeepScanLineInputFile.cpp | 28 +++++++++++++---- + OpenEXR/IlmImf/ImfDeepScanLineOutputFile.cpp | 30 +++++++++++++++++-- + OpenEXR/IlmImf/ImfFrameBuffer.cpp | 4 +-- + OpenEXR/IlmImf/ImfInputFile.cpp | 13 ++++---- + OpenEXR/IlmImf/ImfMisc.cpp | 9 ++++-- + OpenEXR/IlmImf/ImfOutputFile.cpp | 15 ++++++---- + OpenEXR/IlmImf/ImfRgbaFile.cpp | 24 ++++++++++----- + OpenEXR/IlmImf/ImfScanLineInputFile.cpp | 19 +++++++----- + OpenEXR/IlmImf/ImfTiledInputFile.cpp | 5 ++-- + OpenEXR/IlmImf/ImfTiledOutputFile.cpp | 5 ++-- + OpenEXR/IlmImf/ImfTiledRgbaFile.cpp | 13 ++++++-- + .../IlmImfTest/testCompositeDeepScanLine.cpp | 3 +- + OpenEXR/IlmImfTest/testDwaCompressorSimd.cpp | 3 +- + .../IlmImfTest/testLargeDataWindowOffsets.cpp | 16 ++++++++-- + OpenEXR/IlmImfTest/testMultiPartApi.cpp | 15 ++-------- + OpenEXR/IlmImfTest/testMultiPartThreading.cpp | 16 ++-------- + .../testOptimizedInterleavePatterns.cpp | 12 ++++++-- + OpenEXR/IlmImfTest/testPreviewImage.cpp | 2 +- + OpenEXR/IlmImfTest/testSharedFrameBuffer.cpp | 23 ++++++++------ + 25 files changed, 207 insertions(+), 93 deletions(-) + +--- a/IlmImf/ImfInputFile.cpp ++++ b/IlmImf/ImfInputFile.cpp +@@ -302,6 +302,10 @@ bufferedReadPixels (InputFile::Data* ifd + while (modp (yStart, toSlice.ySampling) != 0) + ++yStart; + ++ ++ intptr_t fromBase = reinterpret_cast(fromSlice.base); ++ intptr_t toBase = reinterpret_cast(toSlice.base); ++ + for (int y = yStart; + y <= maxYThisRow; + y += toSlice.ySampling) +@@ -310,14 +314,13 @@ bufferedReadPixels (InputFile::Data* ifd + // Set the pointers to the start of the y scanline in + // this row of tiles + // +- +- fromPtr = fromSlice.base + ++ fromPtr = reinterpret_cast (fromBase + + (y - tileRange.min.y) * fromSlice.yStride + +- xStart * fromSlice.xStride; ++ xStart * fromSlice.xStride); + +- toPtr = toSlice.base + ++ toPtr = reinterpret_cast (toBase + + divp (y, toSlice.ySampling) * toSlice.yStride + +- divp (xStart, toSlice.xSampling) * toSlice.xStride; ++ divp (xStart, toSlice.xSampling) * toSlice.xStride); + + // + // Copy all pixels for the scanline in this row of tiles diff -Nru openexr-2.3.0/debian/patches/series openexr-2.3.0/debian/patches/series --- openexr-2.3.0/debian/patches/series 2020-12-11 13:20:29.000000000 +0000 +++ openexr-2.3.0/debian/patches/series 2021-04-01 12:47:09.000000000 +0000 @@ -39,3 +39,11 @@ CVE-2020-16587.patch CVE-2020-16588.patch CVE-2020-16589.patch +CVE-2021-3474.patch +CVE-2021-3475.patch +CVE-2021-3476.patch +CVE-2021-3477.patch +CVE-2021-3478-pre1.patch +CVE-2021-3478.patch +CVE-2021-3479-pre1.patch +CVE-2021-3479.patch