# HG changeset patch # User andrew # Date 1449091439 0 # Node ID e5e5ed1871a076692795eb82dbd37cf3b04626af # Parent 858cd890a48b7254e819ac77756670fbadb1ecdf 8144071: ImageIO does not reset stream if an exception is thrown Summary: Reset the I/O stream in a finally block Reviewed-by: andrew Contributed-by: Jiri Vanek diff -r 858cd890a48b -r e5e5ed1871a0 jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java --- a/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Wed Dec 02 00:52:49 2015 +0530 +++ b/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Wed Dec 02 21:23:59 2015 +0000 @@ -564,9 +564,12 @@ if (stream != null) { stream.mark(); } - canDecode = spi.canDecodeInput(input); - if (stream != null) { - stream.reset(); + try { + canDecode = spi.canDecodeInput(input); + } finally { + if (stream != null) { + stream.reset(); + } } return canDecode; diff -r 858cd890a48b -r e5e5ed1871a0 jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java Wed Dec 02 21:23:59 2015 +0000 @@ -0,0 +1,367 @@ +/* + * Copyright 2015 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8144071 + * @run main/othervm MarkTryFinallyReproducer + * @summary Test that call to canDecodeInput in ImageIO don't corrupt + * mark/reset stack in ImageInputStream + * @author Jiri Vanek + */ + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Locale; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.IIOByteBuffer; +import javax.imageio.stream.ImageInputStream; + + +public class MarkTryFinallyReproducer { + + private static final byte[] bmp = new byte[]{ + 127,127, 66, 77, -86, 0, 0, 0, 0, 0, 0, 0, + 122, 0, 0, 0, 108, 0, 0, 0, 4, 0, 0, 0, 4, + 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 48, 0, 0, + 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, -1, + -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, -17, + 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1, + -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1 + }; + //first two are evil, we are skipping them later. Others are normal BMP + + private static class NotClosingImageInputStream implements ImageInputStream { + + private final ImageInputStream src; + + private NotClosingImageInputStream(ImageInputStream createImageInputStream) { + this.src = createImageInputStream; + } + + @Override + public void setByteOrder(ByteOrder byteOrder) { + src.setByteOrder(byteOrder); + } + + @Override + public ByteOrder getByteOrder() { + return src.getByteOrder(); + } + + @Override + public int read() throws IOException { + return src.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return src.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return src.read(b, off, len); + } + + @Override + public void readBytes(IIOByteBuffer buf, int len) throws IOException { + src.readBytes(buf, len); + } + + @Override + public boolean readBoolean() throws IOException { + return src.readBoolean(); + } + + @Override + public byte readByte() throws IOException { + return src.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException { + return src.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException { + return src.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + return src.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException { + return src.readChar(); + } + + @Override + public int readInt() throws IOException { + return src.readInt(); + } + + @Override + public long readUnsignedInt() throws IOException { + return src.readUnsignedInt(); + } + + @Override + public long readLong() throws IOException { + return src.readLong(); + } + + @Override + public float readFloat() throws IOException { + return src.readFloat(); + } + + @Override + public double readDouble() throws IOException { + return src.readDouble(); + } + + @Override + public String readLine() throws IOException { + return src.readLine(); + } + + @Override + public String readUTF() throws IOException { + return src.readUTF(); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + src.readFully(b, off, len); + } + + @Override + public void readFully(byte[] b) throws IOException { + src.readFully(b); + } + + @Override + public void readFully(short[] s, int off, int len) throws IOException { + src.readFully(s, off, len); + } + + @Override + public void readFully(char[] c, int off, int len) throws IOException { + src.readFully(c, off, len); + } + + @Override + public void readFully(int[] i, int off, int len) throws IOException { + src.readFully(i, off, len); + } + + @Override + public void readFully(long[] l, int off, int len) throws IOException { + src.readFully(l, off, len); + } + + @Override + public void readFully(float[] f, int off, int len) throws IOException { + src.readFully(f, off, len); + } + + @Override + public void readFully(double[] d, int off, int len) throws IOException { + src.readFully(d, off, len); + } + + @Override + public long getStreamPosition() throws IOException { + return src.getStreamPosition(); + } + + @Override + public int getBitOffset() throws IOException { + return src.getBitOffset(); + } + + @Override + public void setBitOffset(int bitOffset) throws IOException { + src.setBitOffset(bitOffset); + } + + @Override + public int readBit() throws IOException { + return src.readBit(); + } + + @Override + public long readBits(int numBits) throws IOException { + return src.readBits(numBits); + } + + @Override + public long length() throws IOException { + return src.length(); + } + + @Override + public int skipBytes(int n) throws IOException { + return src.skipBytes(n); + } + + @Override + public long skipBytes(long n) throws IOException { + return src.skipBytes(n); + } + + @Override + public void seek(long pos) throws IOException { + src.seek(pos); + } + + @Override + public void mark() { + src.mark(); + } + + @Override + public void reset() throws IOException { + src.reset(); + } + + @Override + public void flushBefore(long pos) throws IOException { + src.flushBefore(pos); + } + + @Override + public void flush() throws IOException { + src.flush(); + } + + @Override + public long getFlushedPosition() { + return src.getFlushedPosition(); + } + + @Override + public boolean isCached() { + return src.isCached(); + } + + @Override + public boolean isCachedMemory() { + return src.isCachedMemory(); + } + + @Override + public boolean isCachedFile() { + return src.isCachedFile(); + } + + @Override + public void close() throws IOException { + //the only important one. nothing + } + } + + static final String readerClassName + = MarkTryFinallyReproducerSpi.class.getName(); + static final String[] localNames = {"myNames"}; + static final String[] localSuffixes = {"mySuffixes"}; + static final String[] localMIMETypes = {"myMimes"}; + + public static class MarkTryFinallyReproducerSpi extends ImageReaderSpi { + + public MarkTryFinallyReproducerSpi() { + super("MarkTryFinallyReproducerSpi", + "1.0", + localNames, + localSuffixes, + localMIMETypes, + readerClassName, + new Class[]{ImageInputStream.class}, + new String[0], + false, + null, + null, + new String[0], + new String[0], + false, + null, + null, + new String[0], + new String[0]); + } + + @Override + public String getDescription(Locale locale) { + return ""; + } + + @Override + public boolean canDecodeInput(Object input) throws IOException { + throw new IOException("Bad luck"); + } + + @Override + public ImageReader createReaderInstance(Object extension) { + return null; + } + } + + public static void main(String[] args) throws IOException { + MarkTryFinallyReproducerSpi spi = new MarkTryFinallyReproducerSpi(); + IIORegistry.getDefaultInstance().registerServiceProvider(spi); + + ImageInputStream iis1 = + new NotClosingImageInputStream(ImageIO.createImageInputStream(new ByteArrayInputStream(bmp))); + iis1.readByte(); + iis1.mark(); + long p1 = iis1.getStreamPosition(); + iis1.readByte(); + iis1.mark(); + long p2 = iis1.getStreamPosition(); + BufferedImage bi1 = ImageIO.read(iis1); + iis1.reset(); + long pn2 = iis1.getStreamPosition(); + iis1.reset(); + long pn1 = iis1.getStreamPosition(); + if (p1 != pn1 || p2!= pn2) { + throw new RuntimeException("Exception from call to canDecodeInput in ImageIO. " + + "Corrupted stack in ImageInputStream"); + } + + } + +}