diff --git a/src/dpx.imageio/libdpx/ReaderInternal.h b/src/dpx.imageio/libdpx/ReaderInternal.h index 124f9405a6..74899ab09e 100644 --- a/src/dpx.imageio/libdpx/ReaderInternal.h +++ b/src/dpx.imageio/libdpx/ReaderInternal.h @@ -172,7 +172,8 @@ namespace dpx BUF *obuf = data + bufoff; int index = (block.x1 * sizeof(U32)) % numberOfComponents; - for (int count = (block.x2 - block.x1 + 1) * numberOfComponents - 1; count >= 0; count--) + const int total = (block.x2 - block.x1 + 1) * numberOfComponents; + for (int count = total - 1; count >= 0; count--) { // unpacking the buffer backwards U16 d1 = U16(readBuf[(count + index) / 3] >> ((2 - (count + index) % 3) * 10 + PADDINGBITS) & 0x3ff); @@ -181,7 +182,10 @@ namespace dpx BaseTypeConverter(d1, obuf[count]); // work-around for 1-channel DPX images - to swap the outlying pixels, otherwise the columns are in the wrong order - if (numberOfComponents == 1 && count % 3 == 0) + // Only when a full group of 3 remains in bounds -- a short trailing + // group (when total isn't a multiple of 3) has no third datum to + // swap with, and obuf[count + 2] would be out of bounds. + if (numberOfComponents == 1 && count % 3 == 0 && count + 2 < total) std::swap(obuf[count], obuf[count + 2]); } #endif diff --git a/testsuite/dpx/ref/out.txt b/testsuite/dpx/ref/out.txt index 88458509f1..d4c13b5d2e 100644 --- a/testsuite/dpx/ref/out.txt +++ b/testsuite/dpx/ref/out.txt @@ -161,3 +161,61 @@ PASS oiiotool ERROR: read : "src/crash-badusersize.dpx": Corrupt userbuf: size claims 4160749568 but whole file size is 2054 Full command line was: > oiiotool src/crash-badusersize.dpx -o test.tif +Reading src/crash-1chan-10bit-filled-methodA.dpx +src/crash-1chan-10bit-filled-methodA.dpx : 80 x 60, 3 channel, uint10 dpx + 2 subimages: 80x60 [u10,u10,u10], 80x60 [u10] + subimage 0: 80 x 60, 3 channel, uint10 dpx + SHA-1: 0071FD79957267E8C9978DB3360AF9C752E31556 + channel list: R, G, B + full/display size: 2143289344 x 2143289344 + full/display origin: 0, 0 + ImageDescription: "view angle: left" + Orientation: 1 (normal) + PixelAspectRatio: 1 + dpx:Colorimetric: "User defined" + dpx:DittoKey: 1 + dpx:EndOfImagePadding: 0 + dpx:EndOfLinePadding: 0 + dpx:FramePosition: 0 + dpx:ImageDescriptor: "RGB" + dpx:Packing: "Filled, method A" + dpx:SequenceLength: 0 + dpx:SourceImageFileName: "P" + dpx:TimeCode: "00:00:00:00" + dpx:Transfer: "Undefined" + dpx:UserBits: 0 + dpx:Version: "V2.0" + dpx:VerticalSampleRate: -1.7147e+38 + dpx:XScannedSize: 1.4013e-45 + dpx:YScannedSize: 1.4013e-45 + oiio:BitsPerSample: 10 + oiio:subimages: 2 + smpte:TimeCode: 00:00:00:00 + subimage 1: 80 x 60, 1 channel, uint10 dpx + SHA-1: 26C5096595B63C5BF3AC347712B11353BB55F1EC + channel list: channel0 + full/display size: 2143289344 x 2143289344 + full/display origin: 0, 0 + ImageDescription: "view angle: left" + Orientation: 1 (normal) + PixelAspectRatio: 1 + dpx:Colorimetric: "User defined" + dpx:DittoKey: 1 + dpx:EndOfImagePadding: 0 + dpx:EndOfLinePadding: 0 + dpx:FramePosition: 0 + dpx:HighQuantity: 7.57306e-29 + dpx:ImageDescriptor: "User defined" + dpx:Packing: "Filled, method A" + dpx:SequenceLength: 0 + dpx:SourceImageFileName: "P" + dpx:TimeCode: "00:00:00:00" + dpx:Transfer: "User defined" + dpx:UserBits: 0 + dpx:Version: "V2.0" + dpx:VerticalSampleRate: -1.7147e+38 + dpx:XScannedSize: 1.4013e-45 + dpx:YScannedSize: 1.4013e-45 + oiio:BitsPerSample: 10 + oiio:subimages: 2 + smpte:TimeCode: 00:00:00:00 diff --git a/testsuite/dpx/run.py b/testsuite/dpx/run.py index 536e705927..1acaee89e7 100755 --- a/testsuite/dpx/run.py +++ b/testsuite/dpx/run.py @@ -37,3 +37,11 @@ # Regression tests command += oiiotool("src/crash-badusersize.dpx -o test.tif", failureok=True) + +# Regression test: crafted DPX with a 1-channel, 10-bit, "Filled method A" +# packed subimage whose width (80) is not a multiple of 3. The 1-channel +# work-around in Read10bitFilled() swapped the first and third datum of +# each group of 3 packed samples, but did not check that a full group of 3 +# remained for the last (partial) group in the scanline, writing/reading +# one uint16 past the end of the caller's scanline buffer. +command += info_command("src/crash-1chan-10bit-filled-methodA.dpx", safematch=True) diff --git a/testsuite/dpx/src/crash-1chan-10bit-filled-methodA.dpx b/testsuite/dpx/src/crash-1chan-10bit-filled-methodA.dpx new file mode 100644 index 0000000000..50579aeab1 Binary files /dev/null and b/testsuite/dpx/src/crash-1chan-10bit-filled-methodA.dpx differ