-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
8, 11, 17, 24, 25
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
When a ZIP file's LOCAL FILE HEADER contains an invalid compression method (LOCHOW), ZipFile.getInputStream() fails to correctly throw a ZipException. The implementation should verify that the compression method in the local header (LOCHOW) matches the one specified in the central directory (CENHOW) and also validate that it's a supported compression method before attempting decompression.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
test passed
ACTUAL -
test failed
---------- BEGIN SOURCE ----------
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* 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 4770745 6218846 6218848 6237956 8313765
* @summary test for correct detection and reporting of corrupted zip files
* @author Martin Buchholz
*/
import java.util.*;
import java.util.zip.*;
import java.io.*;
import static java.lang.System.*;
import static java.util.zip.ZipFile.*;
public class CorruptedZipFiles {
static int passed = 0, failed = 0;
static void fail(String msg) {
failed++;
err.println(msg);
}
static void unexpected(Throwable t) {
failed++;
t.printStackTrace();
}
public static void main(String[] args) throws Exception {
try (FileOutputStream fos = new FileOutputStream("x.zip");
ZipOutputStream zos = new ZipOutputStream(fos))
{
ZipEntry e = new ZipEntry("x");
zos.putNextEntry(e);
zos.write((int)'x');
}
int len = (int)(new File("x.zip").length());
byte[] good = new byte[len];
try (FileInputStream fis = new FileInputStream("x.zip")) {
fis.read(good);
}
new File("x.zip").delete();
int endpos = len - ENDHDR;
int cenpos = u16(good, endpos+ENDOFF);
int locpos = u16(good, cenpos+CENOFF);
if (u32(good, endpos) != ENDSIG) fail("Where's ENDSIG?");
if (u32(good, cenpos) != CENSIG) fail("Where's CENSIG?");
if (u32(good, locpos) != LOCSIG) fail("Where's LOCSIG?");
if (u16(good, locpos+LOCNAM) != u16(good,cenpos+CENNAM))
fail("Name field length mismatch");
if (u16(good, locpos+LOCEXT) != u16(good,cenpos+CENEXT))
fail("Extra field length mismatch");
byte[] bad;
// Test Case: Invalid compression method in local header
err.println("\n========================================");
err.println("TEST: corrupted LOCHOW (invalid compression method in local header)");
bad = good.clone();
bad[locpos + LOCHOW] = 2; // Set invalid method (2)
checkZipExceptionInGetInputStream(bad, ".*invalid compression method.*"); // Fix: Corrected regex to match actual message
out.printf("passed = %d, failed = %d%n", passed, failed);
if (failed > 0) throw new Exception("Some tests failed");
}
static int uniquifier = 432;
static void checkZipExceptionImpl(byte[] data,
String msgPattern,
boolean getInputStream) {
String zipName = "bad" + (uniquifier++) + ".zip";
String testType = getInputStream ? "getInputStream" : "openZipFile";
err.println("== TEST CASE: " + zipName + " (" + testType + ") ==");
err.println("Expected exception pattern: " + msgPattern);
try {
try (FileOutputStream fos = new FileOutputStream(zipName)) {
fos.write(data);
}
try (ZipFile zf = new ZipFile(zipName)) {
if (getInputStream) {
err.println(" Attempting to get input stream...");
InputStream is = zf.getInputStream(new ZipEntry("x"));
is.read();
} else {
err.println(" ZipFile opened successfully without exception");
}
}
err.println(" TEST FAILED: No exception thrown");
fail("Failed to throw expected ZipException");
} catch (ZipException e) {
err.println(" Caught ZipException: \"" + e.getMessage() + "\"");
if (e.getMessage().matches(msgPattern)) {
err.println(" TEST PASSED: Exception matches expected pattern");
passed++;
} else {
err.println(" TEST FAILED: Exception message doesn't match pattern");
err.println(" Actual: \"" + e.getMessage() + "\"");
err.println(" Expected pattern: \"" + msgPattern + "\"");
unexpected(e);
}
} catch (Throwable t) {
err.println(" TEST FAILED: Caught unexpected exception type: " + t.getClass().getName());
unexpected(t);
} finally {
new File(zipName).delete();
}
err.println();
}
static void checkZipException(byte[] data, String msgPattern) {
checkZipExceptionImpl(data, msgPattern, false);
}
static void checkZipExceptionInGetInputStream(byte[] data, String msgPattern) {
checkZipExceptionImpl(data, msgPattern, true);
}
static int u8(byte[] data, int offset) {
return data[offset]&0xff;
}
static int u16(byte[] data, int offset) {
return u8(data,offset) + (u8(data,offset+1)<<8);
}
static int u32(byte[] data, int offset) {
return u16(data,offset) + (u16(data,offset+2)<<16);
}
// The following can be deleted once this bug is fixed:
// 6225935: "import static" accessibility rules for symbols different for no reason
static final long LOCSIG = ZipFile.LOCSIG;
static final long EXTSIG = ZipFile.EXTSIG;
static final long CENSIG = ZipFile.CENSIG;
static final long ENDSIG = ZipFile.ENDSIG;
static final int LOCHDR = ZipFile.LOCHDR;
static final int EXTHDR = ZipFile.EXTHDR;
static final int CENHDR = ZipFile.CENHDR;
static final int ENDHDR = ZipFile.ENDHDR;
static final int LOCVER = ZipFile.LOCVER;
static final int LOCFLG = ZipFile.LOCFLG;
static final int LOCHOW = ZipFile.LOCHOW;
static final int LOCTIM = ZipFile.LOCTIM;
static final int LOCCRC = ZipFile.LOCCRC;
static final int LOCSIZ = ZipFile.LOCSIZ;
static final int LOCLEN = ZipFile.LOCLEN;
static final int LOCNAM = ZipFile.LOCNAM;
static final int LOCEXT = ZipFile.LOCEXT;
static final int CENVEM = ZipFile.CENVEM;
static final int CENVER = ZipFile.CENVER;
static final int CENFLG = ZipFile.CENFLG;
static final int CENHOW = ZipFile.CENHOW;
static final int CENTIM = ZipFile.CENTIM;
static final int CENCRC = ZipFile.CENCRC;
static final int CENSIZ = ZipFile.CENSIZ;
static final int CENLEN = ZipFile.CENLEN;
static final int CENNAM = ZipFile.CENNAM;
static final int CENEXT = ZipFile.CENEXT;
static final int CENCOM = ZipFile.CENCOM;
static final int CENDSK = ZipFile.CENDSK;
static final int CENATT = ZipFile.CENATT;
static final int CENATX = ZipFile.CENATX;
static final int CENOFF = ZipFile.CENOFF;
static final int ENDSUB = ZipFile.ENDSUB;
static final int ENDTOT = ZipFile.ENDTOT;
static final int ENDSIZ = ZipFile.ENDSIZ;
static final int ENDOFF = ZipFile.ENDOFF;
static final int ENDCOM = ZipFile.ENDCOM;
}
---------- END SOURCE ----------
When a ZIP file's LOCAL FILE HEADER contains an invalid compression method (LOCHOW), ZipFile.getInputStream() fails to correctly throw a ZipException. The implementation should verify that the compression method in the local header (LOCHOW) matches the one specified in the central directory (CENHOW) and also validate that it's a supported compression method before attempting decompression.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
test passed
ACTUAL -
test failed
---------- BEGIN SOURCE ----------
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* 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 4770745 6218846 6218848 6237956 8313765
* @summary test for correct detection and reporting of corrupted zip files
* @author Martin Buchholz
*/
import java.util.*;
import java.util.zip.*;
import java.io.*;
import static java.lang.System.*;
import static java.util.zip.ZipFile.*;
public class CorruptedZipFiles {
static int passed = 0, failed = 0;
static void fail(String msg) {
failed++;
err.println(msg);
}
static void unexpected(Throwable t) {
failed++;
t.printStackTrace();
}
public static void main(String[] args) throws Exception {
try (FileOutputStream fos = new FileOutputStream("x.zip");
ZipOutputStream zos = new ZipOutputStream(fos))
{
ZipEntry e = new ZipEntry("x");
zos.putNextEntry(e);
zos.write((int)'x');
}
int len = (int)(new File("x.zip").length());
byte[] good = new byte[len];
try (FileInputStream fis = new FileInputStream("x.zip")) {
fis.read(good);
}
new File("x.zip").delete();
int endpos = len - ENDHDR;
int cenpos = u16(good, endpos+ENDOFF);
int locpos = u16(good, cenpos+CENOFF);
if (u32(good, endpos) != ENDSIG) fail("Where's ENDSIG?");
if (u32(good, cenpos) != CENSIG) fail("Where's CENSIG?");
if (u32(good, locpos) != LOCSIG) fail("Where's LOCSIG?");
if (u16(good, locpos+LOCNAM) != u16(good,cenpos+CENNAM))
fail("Name field length mismatch");
if (u16(good, locpos+LOCEXT) != u16(good,cenpos+CENEXT))
fail("Extra field length mismatch");
byte[] bad;
// Test Case: Invalid compression method in local header
err.println("\n========================================");
err.println("TEST: corrupted LOCHOW (invalid compression method in local header)");
bad = good.clone();
bad[locpos + LOCHOW] = 2; // Set invalid method (2)
checkZipExceptionInGetInputStream(bad, ".*invalid compression method.*"); // Fix: Corrected regex to match actual message
out.printf("passed = %d, failed = %d%n", passed, failed);
if (failed > 0) throw new Exception("Some tests failed");
}
static int uniquifier = 432;
static void checkZipExceptionImpl(byte[] data,
String msgPattern,
boolean getInputStream) {
String zipName = "bad" + (uniquifier++) + ".zip";
String testType = getInputStream ? "getInputStream" : "openZipFile";
err.println("== TEST CASE: " + zipName + " (" + testType + ") ==");
err.println("Expected exception pattern: " + msgPattern);
try {
try (FileOutputStream fos = new FileOutputStream(zipName)) {
fos.write(data);
}
try (ZipFile zf = new ZipFile(zipName)) {
if (getInputStream) {
err.println(" Attempting to get input stream...");
InputStream is = zf.getInputStream(new ZipEntry("x"));
is.read();
} else {
err.println(" ZipFile opened successfully without exception");
}
}
err.println(" TEST FAILED: No exception thrown");
fail("Failed to throw expected ZipException");
} catch (ZipException e) {
err.println(" Caught ZipException: \"" + e.getMessage() + "\"");
if (e.getMessage().matches(msgPattern)) {
err.println(" TEST PASSED: Exception matches expected pattern");
passed++;
} else {
err.println(" TEST FAILED: Exception message doesn't match pattern");
err.println(" Actual: \"" + e.getMessage() + "\"");
err.println(" Expected pattern: \"" + msgPattern + "\"");
unexpected(e);
}
} catch (Throwable t) {
err.println(" TEST FAILED: Caught unexpected exception type: " + t.getClass().getName());
unexpected(t);
} finally {
new File(zipName).delete();
}
err.println();
}
static void checkZipException(byte[] data, String msgPattern) {
checkZipExceptionImpl(data, msgPattern, false);
}
static void checkZipExceptionInGetInputStream(byte[] data, String msgPattern) {
checkZipExceptionImpl(data, msgPattern, true);
}
static int u8(byte[] data, int offset) {
return data[offset]&0xff;
}
static int u16(byte[] data, int offset) {
return u8(data,offset) + (u8(data,offset+1)<<8);
}
static int u32(byte[] data, int offset) {
return u16(data,offset) + (u16(data,offset+2)<<16);
}
// The following can be deleted once this bug is fixed:
// 6225935: "import static" accessibility rules for symbols different for no reason
static final long LOCSIG = ZipFile.LOCSIG;
static final long EXTSIG = ZipFile.EXTSIG;
static final long CENSIG = ZipFile.CENSIG;
static final long ENDSIG = ZipFile.ENDSIG;
static final int LOCHDR = ZipFile.LOCHDR;
static final int EXTHDR = ZipFile.EXTHDR;
static final int CENHDR = ZipFile.CENHDR;
static final int ENDHDR = ZipFile.ENDHDR;
static final int LOCVER = ZipFile.LOCVER;
static final int LOCFLG = ZipFile.LOCFLG;
static final int LOCHOW = ZipFile.LOCHOW;
static final int LOCTIM = ZipFile.LOCTIM;
static final int LOCCRC = ZipFile.LOCCRC;
static final int LOCSIZ = ZipFile.LOCSIZ;
static final int LOCLEN = ZipFile.LOCLEN;
static final int LOCNAM = ZipFile.LOCNAM;
static final int LOCEXT = ZipFile.LOCEXT;
static final int CENVEM = ZipFile.CENVEM;
static final int CENVER = ZipFile.CENVER;
static final int CENFLG = ZipFile.CENFLG;
static final int CENHOW = ZipFile.CENHOW;
static final int CENTIM = ZipFile.CENTIM;
static final int CENCRC = ZipFile.CENCRC;
static final int CENSIZ = ZipFile.CENSIZ;
static final int CENLEN = ZipFile.CENLEN;
static final int CENNAM = ZipFile.CENNAM;
static final int CENEXT = ZipFile.CENEXT;
static final int CENCOM = ZipFile.CENCOM;
static final int CENDSK = ZipFile.CENDSK;
static final int CENATT = ZipFile.CENATT;
static final int CENATX = ZipFile.CENATX;
static final int CENOFF = ZipFile.CENOFF;
static final int ENDSUB = ZipFile.ENDSUB;
static final int ENDTOT = ZipFile.ENDTOT;
static final int ENDSIZ = ZipFile.ENDSIZ;
static final int ENDOFF = ZipFile.ENDOFF;
static final int ENDCOM = ZipFile.ENDCOM;
}
---------- END SOURCE ----------