#713 PSD: Broken uncompressed reading from stream w/unknown length

(cherry picked from commit da800be8c8d422321d0f23f04e8e72b1b4c79117)
This commit is contained in:
Harald Kuhr 2022-11-10 16:11:34 +01:00
parent 1e981242ad
commit 0538db7103
3 changed files with 48 additions and 22 deletions

View File

@ -53,20 +53,20 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
/** /**
* Creates a {@link ImageInputStream}, reading up to a maximum number of bytes from the underlying stream. * Creates a {@link ImageInputStream}, reading up to a maximum number of bytes from the underlying stream.
* *
* @param pStream the underlying stream * @param stream the underlying stream
* @param pLength the maximum length to read from the stream. * @param length the maximum length to read from the stream.
* Note that {@code pStream} may contain less than this maximum number of bytes. * Note that {@code stream} may contain less than this maximum number of bytes.
* *
* @throws IOException if {@code pStream}'s position can't be determined. * @throws IOException if {@code stream}'s position can't be determined.
* @throws IllegalArgumentException if {@code pStream == null} or {@code pLength < 0} * @throws IllegalArgumentException if {@code stream == null} or {@code length < 0}
*/ */
public SubImageInputStream(final ImageInputStream pStream, final long pLength) throws IOException { public SubImageInputStream(final ImageInputStream stream, final long length) throws IOException {
Validate.notNull(pStream, "stream"); Validate.notNull(stream, "stream");
Validate.isTrue(pLength >= 0, pLength, "length < 0: %d"); Validate.isTrue(length >= 0, length, "length < 0: %d");
stream = pStream; this.stream = stream;
startPos = pStream.getStreamPosition(); this.startPos = stream.getStreamPosition();
length = pLength; this.length = length;
} }
public int read() throws IOException { public int read() throws IOException {
@ -84,14 +84,14 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
} }
} }
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException { public int read(final byte[] bytes, final int off, final int len) throws IOException {
if (streamPos >= length) { // Local EOF if (streamPos >= length) { // Local EOF
return -1; return -1;
} }
// Safe cast, as pLength can never cause int overflow // Safe cast, as len can never cause int overflow
int length = (int) Math.min(pLength, this.length - streamPos); int length = (int) Math.min(len, this.length - streamPos);
int count = stream.read(pBytes, pOffset, length); int count = stream.read(bytes, off, length);
if (count >= 0) { if (count >= 0) {
streamPos += count; streamPos += count;
@ -113,18 +113,18 @@ public final class SubImageInputStream extends ImageInputStreamImpl {
} }
@Override @Override
public void seek(final long pPosition) throws IOException { public void seek(final long position) throws IOException {
if (pPosition < getFlushedPosition()) { if (position < getFlushedPosition()) {
throw new IndexOutOfBoundsException("pos < flushedPosition"); throw new IndexOutOfBoundsException("pos < flushedPosition");
} }
stream.seek(startPos + pPosition); stream.seek(startPos + position);
streamPos = pPosition; streamPos = position;
} }
@SuppressWarnings({"FinalizeDoesntCallSuperFinalize"}) @SuppressWarnings("MethodDoesntCallSuperMethod")
@Override @Override
protected void finalize() throws Throwable { protected void finalize() {
// Empty finalizer (for improved performance; no need to call super.finalize() in this case) // Empty finalizer (for improved performance; no need to call super.finalize() in this case)
} }
} }

View File

@ -103,7 +103,8 @@ final class PSDUtil {
final int[] byteCounts, long compressedLength) throws IOException { final int[] byteCounts, long compressedLength) throws IOException {
switch (compression) { switch (compression) {
case PSD.COMPRESSION_NONE: case PSD.COMPRESSION_NONE:
return new SubImageInputStream(stream, stream.length()); long streamLength = stream.length();
return new SubImageInputStream(stream, streamLength < 0 ? Long.MAX_VALUE : streamLength);
case PSD.COMPRESSION_RLE: case PSD.COMPRESSION_RLE:
return new DirectImageInputStream(new SequenceInputStream(new LazyPackBitsStreamEnumeration(byteCounts, stream))); return new DirectImageInputStream(new SequenceInputStream(new LazyPackBitsStreamEnumeration(byteCounts, stream)));

View File

@ -31,10 +31,12 @@
package com.twelvemonkeys.imageio.plugins.psd; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.DirectImageInputStream;
import org.junit.Test; import org.junit.Test;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import static com.twelvemonkeys.imageio.plugins.psd.PSDUtil.createDecompressorStream; import static com.twelvemonkeys.imageio.plugins.psd.PSDUtil.createDecompressorStream;
@ -65,6 +67,29 @@ public class PSDUtilDecompressorStreamTest {
} }
} }
@Test
public void testUncompressedUnknownLength() throws IOException {
// Data represents 3 x 3 raster with 8 bit samples, all 0x7f's
byte[] data = new byte[] {
0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f
};
try (ImageInputStream input = createDecompressorStream(new DirectImageInputStream(new ByteArrayInputStream(data)), PSD.COMPRESSION_NONE, 3, 8, null, 9)) {
byte[] row = new byte[3];
for (int y = 0; y < 3; y++) {
input.readFully(row);
for (byte b : row) {
assertEquals((byte) 0x7f, b);
}
}
assertEquals(-1, input.read());
}
}
@Test @Test
public void testPackBits() throws IOException { public void testPackBits() throws IOException {
// Data represents 3 x 3 raster with 8 bit samples, all 42's // Data represents 3 x 3 raster with 8 bit samples, all 42's