-
Bug
-
Resolution: Unresolved
-
P4
-
8, 25
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
When reading a Zip entry that uses the STORED compression method with a size of 0, if its CRC value is tampered with, ZipInputStream fails to properly trigger a CRC verification exception.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.util.zip.*;
public class StoredCRC {
public static void realMain(String[] args) throws Throwable {
try {
// Valid empty entry
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("empty");
ze.setMethod(ZipOutputStream.STORED);
ze.setSize(0);
CRC32 crc = new CRC32();
crc.update(new byte[0]);
ze.setCrc(crc.getValue());
System.out.println("real crc: " + crc.getValue());
zos.putNextEntry(ze);
zos.close();
// Verify valid read
ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
check(zis.getNextEntry() != null);
check(zis.read() == -1); // Immediate EOF
// Corrupt empty entry's CRC
byte[] corrupted = baos.toByteArray();
corrupted[14] ^= 1; // Toggle CRC byte
zis = new ZipInputStream(new ByteArrayInputStream(corrupted));
ze = zis.getNextEntry();
check(ze != null);
check(zis.read() == -1); // Read to EOF
System.out.println(ze);
System.out.println("Changed crc: " + ze.getCrc());
System.out.println();
try {
zis.getNextEntry();
zis.closeEntry();
zis.close();
fail("Empty entry: No CRC check for zero-length file");
} catch (ZipException ex) {
check(ex.getMessage().startsWith("invalid entry CRC (expected 0x"));
}
} catch (Throwable t) {
unexpected(t);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static boolean pass() {passed++; return true;}
static boolean fail() {failed++; Thread.dumpStack(); return false;}
static boolean fail(String msg) {System.out.println(msg); return fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) return pass();
else return fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
---------- END SOURCE ----------
When reading a Zip entry that uses the STORED compression method with a size of 0, if its CRC value is tampered with, ZipInputStream fails to properly trigger a CRC verification exception.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.util.zip.*;
public class StoredCRC {
public static void realMain(String[] args) throws Throwable {
try {
// Valid empty entry
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("empty");
ze.setMethod(ZipOutputStream.STORED);
ze.setSize(0);
CRC32 crc = new CRC32();
crc.update(new byte[0]);
ze.setCrc(crc.getValue());
System.out.println("real crc: " + crc.getValue());
zos.putNextEntry(ze);
zos.close();
// Verify valid read
ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
check(zis.getNextEntry() != null);
check(zis.read() == -1); // Immediate EOF
// Corrupt empty entry's CRC
byte[] corrupted = baos.toByteArray();
corrupted[14] ^= 1; // Toggle CRC byte
zis = new ZipInputStream(new ByteArrayInputStream(corrupted));
ze = zis.getNextEntry();
check(ze != null);
check(zis.read() == -1); // Read to EOF
System.out.println(ze);
System.out.println("Changed crc: " + ze.getCrc());
System.out.println();
try {
zis.getNextEntry();
zis.closeEntry();
zis.close();
fail("Empty entry: No CRC check for zero-length file");
} catch (ZipException ex) {
check(ex.getMessage().startsWith("invalid entry CRC (expected 0x"));
}
} catch (Throwable t) {
unexpected(t);
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static boolean pass() {passed++; return true;}
static boolean fail() {failed++; Thread.dumpStack(); return false;}
static boolean fail(String msg) {System.out.println(msg); return fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
static boolean equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) return pass();
else return fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
---------- END SOURCE ----------