1 /*
2  * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
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 8193682 8278794 8284771
27  * @summary Test Infinite loop while writing on closed Deflater and Inflater.
28  * @run testng CloseInflaterDeflaterTest
29  */
30 package test.java.util.zip;
31 
32 import java.io.*;
33 import java.util.Random;
34 import java.util.jar.JarOutputStream;
35 import java.util.zip.DeflaterInputStream;
36 import java.util.zip.DeflaterOutputStream;
37 import java.util.zip.GZIPOutputStream;
38 import java.util.zip.InflaterOutputStream;
39 import java.util.zip.ZipOutputStream;
40 import java.util.zip.ZipEntry;
41 
42 import org.testng.annotations.BeforeTest;
43 import org.testng.annotations.DataProvider;
44 import org.testng.annotations.Test;
45 import static org.testng.Assert.assertThrows;
46 
47 
48 public class CloseInflaterDeflaterTest {
49 
50     // Number of bytes to write/read from Deflater/Inflater
51     private static final int INPUT_LENGTH= 512;
52     // OutputStream that will throw an exception during a write operation
53     private static OutputStream outStream = new OutputStream() {
54         @Override
55         public void write(byte[] b, int off, int len) throws IOException {
56             throw new IOException();
57         }
58         @Override
59         public void write(byte[] b) throws IOException {}
60         @Override
61         public void write(int b) throws IOException {}
62     };
63     // InputStream that will throw an exception during a read operation
64     private static InputStream inStream = new InputStream() {
65         @Override
66         public int read(byte[] b, int off, int len) throws IOException {
67             throw new IOException();
68         }
69         @Override
70         public int read(byte[] b) throws IOException { throw new IOException();}
71         @Override
72         public int read() throws IOException { throw new IOException();}
73     };
74     // Input bytes for read/write operation
75     private static byte[] inputBytes = new byte[INPUT_LENGTH];
76     // Random function to add bytes to inputBytes
77     private static Random rand = new Random();
78 
79     /**
80      * DataProvider to specify whether to use close() or finish() of OutputStream
81      *
82      * @return Entry object indicating which method to use for closing OutputStream
83      */
84     @DataProvider
testOutputStreams()85     public Object[][] testOutputStreams() {
86      return new Object[][] {
87       { true },
88       { false },
89      };
90     }
91 
92     /**
93      * DataProvider to specify on which outputstream closeEntry() has to be called
94      *
95      * @return Entry object returning either JarOutputStream or ZipOutputStream
96      */
97     @DataProvider
testZipAndJar()98     public Object[][] testZipAndJar() throws IOException{
99      return new Object[][] {
100       { new JarOutputStream(outStream)},
101       { new ZipOutputStream(outStream)},
102      };
103     }
104 
105     /**
106      * Add inputBytes array with random bytes to write into OutputStream
107      */
108     @BeforeTest
before_test()109     public void before_test()
110     {
111        rand.nextBytes(inputBytes);
112     }
113 
114     /**
115      * Test for infinite loop by writing bytes to closed GZIPOutputStream
116      *
117      * @param useCloseMethod indicates whether to use Close() or finish() method
118      * @throws IOException if an error occurs
119      */
120     @Test(dataProvider = "testOutputStreams")
testGZip(boolean useCloseMethod)121     public void testGZip(boolean useCloseMethod) throws IOException {
122         GZIPOutputStream gzip = new GZIPOutputStream(outStream);
123         gzip.write(inputBytes, 0, INPUT_LENGTH);
124         assertThrows(IOException.class, () -> {
125             // Close GZIPOutputStream
126             if (useCloseMethod) {
127                 gzip.close();
128             } else {
129                 gzip.finish();
130             }
131         });
132         // Write on a closed GZIPOutputStream, closed Deflater IOException expected
133         assertThrows(NullPointerException.class , () -> gzip.write(inputBytes, 0, INPUT_LENGTH));
134     }
135 
136     /**
137      * Test for infinite loop by writing bytes to closed DeflaterOutputStream
138      *
139      * @param useCloseMethod indicates whether to use Close() or finish() method
140      * @throws IOException if an error occurs
141      */
142     @Test(dataProvider = "testOutputStreams")
testDeflaterOutputStream(boolean useCloseMethod)143     public void testDeflaterOutputStream(boolean useCloseMethod) throws IOException {
144         DeflaterOutputStream def = new DeflaterOutputStream(outStream);
145         assertThrows(IOException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
146         assertThrows(IOException.class, () -> {
147             // Close DeflaterOutputStream
148             if (useCloseMethod) {
149                 def.close();
150             } else {
151                 def.finish();
152             }
153         });
154         // Write on a closed DeflaterOutputStream, 'Deflater has been closed' NPE is expected
155         assertThrows(NullPointerException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
156     }
157 
158     /**
159      * Test for infinite loop by reading bytes from closed DeflaterInputStream
160      *
161      * @throws IOException if an error occurs
162      */
163     @Test
testDeflaterInputStream()164     public void testDeflaterInputStream() throws IOException {
165         DeflaterInputStream def = new DeflaterInputStream(inStream);
166         assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
167         // Close DeflaterInputStream
168         def.close();
169         // Read from a closed DeflaterInputStream, closed Deflater IOException expected
170         assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
171     }
172 
173     /**
174      * Test for infinite loop by writing bytes to closed InflaterOutputStream
175      *
176      * Note: Disabling this test as it is failing intermittently.
177      * @param useCloseMethod indicates whether to use Close() or finish() method
178      * @throws IOException if an error occurs
179      */
180     @Test(dataProvider = "testOutputStreams",enabled=false)
testInflaterOutputStream(boolean useCloseMethod)181     public void testInflaterOutputStream(boolean useCloseMethod) throws IOException {
182         InflaterOutputStream inf = new InflaterOutputStream(outStream);
183         assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
184         assertThrows(IOException.class , () -> {
185             // Close InflaterOutputStream
186             if (useCloseMethod) {
187                 inf.close();
188             } else {
189                 inf.finish();
190             }
191         });
192         // Write on a closed InflaterOutputStream , closed Inflater IOException expected
193         assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
194     }
195 
196     /**
197      * Test for infinite loop by writing bytes to closed ZipOutputStream/JarOutputStream
198      *
199      * @param zip will be the instance of either JarOutputStream or ZipOutputStream
200      * @throws IOException if an error occurs
201      */
202     @Test(dataProvider = "testZipAndJar")
testZipCloseEntry(ZipOutputStream zip)203     public void testZipCloseEntry(ZipOutputStream zip) throws IOException {
204         assertThrows(IOException.class , () -> zip.putNextEntry(new ZipEntry("")));
205         zip.write(inputBytes, 0, INPUT_LENGTH);
206         assertThrows(IOException.class , () -> zip.closeEntry());
207         // Write on a closed ZipOutputStream , 'Deflater has been closed' NPE is expected
208         assertThrows(NullPointerException.class , () -> zip.write(inputBytes, 0, INPUT_LENGTH));
209     }
210 
211 }
212