1/*
2 * Copyright 2015 Red Hat, Inc.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8144071
27 * @run main/othervm MarkTryFinallyReproducer
28 * @summary Test that call to canDecodeInput in ImageIO don't corrupt
29 *           mark/reset stack in ImageInputStream
30 * @author Jiri Vanek
31 */
32
33import java.awt.image.BufferedImage;
34import java.io.ByteArrayInputStream;
35import java.io.IOException;
36import java.nio.ByteOrder;
37import java.util.Locale;
38import javax.imageio.ImageIO;
39import javax.imageio.ImageReader;
40import javax.imageio.spi.IIORegistry;
41import javax.imageio.spi.ImageReaderSpi;
42import javax.imageio.stream.IIOByteBuffer;
43import javax.imageio.stream.ImageInputStream;
44
45
46public class MarkTryFinallyReproducer {
47
48    private static final byte[] bmp = new byte[]{
49        127,127, 66, 77, -86, 0, 0, 0, 0, 0, 0, 0,
50        122, 0, 0, 0, 108, 0, 0, 0, 4, 0, 0, 0, 4,
51        0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 48, 0, 0,
52        0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
53        0, 0, 0, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0,
54        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56        0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
57        0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, -1,
58        -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, -17,
59        0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1,
60        -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1
61    };
62    //first two are evil, we are skipping them later. Others are normal BMP
63
64    private static class NotClosingImageInputStream implements ImageInputStream {
65
66        private final ImageInputStream src;
67
68        private NotClosingImageInputStream(ImageInputStream createImageInputStream) {
69            this.src = createImageInputStream;
70        }
71
72        @Override
73        public void setByteOrder(ByteOrder byteOrder) {
74            src.setByteOrder(byteOrder);
75        }
76
77        @Override
78        public ByteOrder getByteOrder() {
79            return src.getByteOrder();
80        }
81
82        @Override
83        public int read() throws IOException {
84            return src.read();
85        }
86
87        @Override
88        public int read(byte[] b) throws IOException {
89            return src.read(b);
90        }
91
92        @Override
93        public int read(byte[] b, int off, int len) throws IOException {
94            return src.read(b, off, len);
95        }
96
97        @Override
98        public void readBytes(IIOByteBuffer buf, int len) throws IOException {
99            src.readBytes(buf, len);
100        }
101
102        @Override
103        public boolean readBoolean() throws IOException {
104            return src.readBoolean();
105        }
106
107        @Override
108        public byte readByte() throws IOException {
109            return src.readByte();
110        }
111
112        @Override
113        public int readUnsignedByte() throws IOException {
114            return src.readUnsignedByte();
115        }
116
117        @Override
118        public short readShort() throws IOException {
119            return src.readShort();
120        }
121
122        @Override
123        public int readUnsignedShort() throws IOException {
124            return src.readUnsignedShort();
125        }
126
127        @Override
128        public char readChar() throws IOException {
129            return src.readChar();
130        }
131
132        @Override
133        public int readInt() throws IOException {
134            return src.readInt();
135        }
136
137        @Override
138        public long readUnsignedInt() throws IOException {
139            return src.readUnsignedInt();
140        }
141
142        @Override
143        public long readLong() throws IOException {
144            return src.readLong();
145        }
146
147        @Override
148        public float readFloat() throws IOException {
149            return src.readFloat();
150        }
151
152        @Override
153        public double readDouble() throws IOException {
154            return src.readDouble();
155        }
156
157        @Override
158        public String readLine() throws IOException {
159            return src.readLine();
160        }
161
162        @Override
163        public String readUTF() throws IOException {
164            return src.readUTF();
165        }
166
167        @Override
168        public void readFully(byte[] b, int off, int len) throws IOException {
169            src.readFully(b, off, len);
170        }
171
172        @Override
173        public void readFully(byte[] b) throws IOException {
174            src.readFully(b);
175        }
176
177        @Override
178        public void readFully(short[] s, int off, int len) throws IOException {
179            src.readFully(s, off, len);
180        }
181
182        @Override
183        public void readFully(char[] c, int off, int len) throws IOException {
184            src.readFully(c, off, len);
185        }
186
187        @Override
188        public void readFully(int[] i, int off, int len) throws IOException {
189            src.readFully(i, off, len);
190        }
191
192        @Override
193        public void readFully(long[] l, int off, int len) throws IOException {
194            src.readFully(l, off, len);
195        }
196
197        @Override
198        public void readFully(float[] f, int off, int len) throws IOException {
199            src.readFully(f, off, len);
200        }
201
202        @Override
203        public void readFully(double[] d, int off, int len) throws IOException {
204            src.readFully(d, off, len);
205        }
206
207        @Override
208        public long getStreamPosition() throws IOException {
209            return src.getStreamPosition();
210        }
211
212        @Override
213        public int getBitOffset() throws IOException {
214            return src.getBitOffset();
215        }
216
217        @Override
218        public void setBitOffset(int bitOffset) throws IOException {
219            src.setBitOffset(bitOffset);
220        }
221
222        @Override
223        public int readBit() throws IOException {
224            return src.readBit();
225        }
226
227        @Override
228        public long readBits(int numBits) throws IOException {
229            return src.readBits(numBits);
230        }
231
232        @Override
233        public long length() throws IOException {
234            return src.length();
235        }
236
237        @Override
238        public int skipBytes(int n) throws IOException {
239            return src.skipBytes(n);
240        }
241
242        @Override
243        public long skipBytes(long n) throws IOException {
244            return src.skipBytes(n);
245        }
246
247        @Override
248        public void seek(long pos) throws IOException {
249            src.seek(pos);
250        }
251
252        @Override
253        public void mark() {
254            src.mark();
255        }
256
257        @Override
258        public void reset() throws IOException {
259            src.reset();
260        }
261
262        @Override
263        public void flushBefore(long pos) throws IOException {
264            src.flushBefore(pos);
265        }
266
267        @Override
268        public void flush() throws IOException {
269            src.flush();
270        }
271
272        @Override
273        public long getFlushedPosition() {
274            return src.getFlushedPosition();
275        }
276
277        @Override
278        public boolean isCached() {
279            return src.isCached();
280        }
281
282        @Override
283        public boolean isCachedMemory() {
284            return src.isCachedMemory();
285        }
286
287        @Override
288        public boolean isCachedFile() {
289            return src.isCachedFile();
290        }
291
292        @Override
293        public void close() throws IOException {
294            //the only important one. nothing
295        }
296    }
297
298    static final String readerClassName
299            = MarkTryFinallyReproducerSpi.class.getName();
300    static final String[] localNames = {"myNames"};
301    static final String[] localSuffixes = {"mySuffixes"};
302    static final String[] localMIMETypes = {"myMimes"};
303
304    public static class MarkTryFinallyReproducerSpi extends ImageReaderSpi {
305
306        public MarkTryFinallyReproducerSpi() {
307            super("MarkTryFinallyReproducerSpi",
308                    "1.0",
309                    localNames,
310                    localSuffixes,
311                    localMIMETypes,
312                    readerClassName,
313                    new Class[]{ImageInputStream.class},
314                    new String[0],
315                    false,
316                    null,
317                    null,
318                    new String[0],
319                    new String[0],
320                    false,
321                    null,
322                    null,
323                    new String[0],
324                    new String[0]);
325        }
326
327        @Override
328        public String getDescription(Locale locale) {
329            return "";
330        }
331
332        @Override
333        public boolean canDecodeInput(Object input) throws IOException {
334            throw new IOException("Bad luck");
335        }
336
337        @Override
338        public ImageReader createReaderInstance(Object extension) {
339            return null;
340        }
341    }
342
343    public static void main(String[] args) throws IOException {
344        MarkTryFinallyReproducerSpi spi = new MarkTryFinallyReproducerSpi();
345        IIORegistry.getDefaultInstance().registerServiceProvider(spi);
346
347        ImageInputStream iis1 =
348          new NotClosingImageInputStream(ImageIO.createImageInputStream(new ByteArrayInputStream(bmp)));
349        iis1.readByte();
350        iis1.mark();
351        long p1 = iis1.getStreamPosition();
352        iis1.readByte();
353        iis1.mark();
354        long p2 = iis1.getStreamPosition();
355        BufferedImage bi1 = ImageIO.read(iis1);
356        iis1.reset();
357        long pn2 = iis1.getStreamPosition();
358        iis1.reset();
359        long pn1 = iis1.getStreamPosition();
360        if (p1 != pn1 || p2!= pn2) {
361            throw new RuntimeException("Exception from call to canDecodeInput in ImageIO. " +
362                                       "Corrupted stack in ImageInputStream");
363        }
364
365    }
366
367}
368