JDK-4879507 : ZipInputStream does not check CRC for stored (uncompressed) files
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.jar
  • Affected Version: 1.4.1,1.4.2_06
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_9,windows_xp
  • CPU: x86,sparc
  • Submitted: 2003-06-16
  • Updated: 2010-07-09
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7
1.4.2_15Fixed 6u4Fixed 7 b05Fixed
Related Reports
Relates :  
Relates :  
Description
Name: nt126004			Date: 06/16/2003


FULL PRODUCT VERSION :
JDK 1.4.1_01-b01

FULL OS VERSION :
WindowsXP

A DESCRIPTION OF THE PROBLEM :
java.util.zip.ZipInputStream does not check CRCs for STORED (uncompressed) files. Corrupted archives with wrong CRCs are read without throwing an exception.

I've tracked this in the sources too :

read(byte[],int,int) method in the ZipInputStream class has two case branches. When current zip entry is read to the end, the 'DEFLATED' case makes a call to private readEnd(ZipEntry) method which checks CRC and throws and exception if it is invalid. However, the 'STORED' case does not call the readEnd(ZipEntry) method and does not check CRC on its own.

I've changed the archive content using a hex editor. I didn't change
anything in headers, since this would be detected and I didn't change
file length as this is also properly handled in ZipInputStream and would
cause an exception. The point is to change a few bytes so that the only
way to detect it is using CRCs. 

I've prepared an example.

I took README.txt file from JRE distribution. I've archived it using the
following command :

jar cf0 testjar.jar README.txt

I've opened created archive in a hex editor and changed string "Java(TM) 2"
to "Java(TM) 3".

Now, archivers such as WinZip report a CRC error and refuse to open this
archive. ZipInputStream does not report any problems.

When I extract the README.txt from this archive using this command :

jar xf testjar.jar

I get a nice README.txt file refering to Java 3 Runtime environment !!!!!


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Take arbitrary uncompressed JAR file and corrupt it using a hex-editor. Make sure headers are not changed and file length is not changed.

Use the jar command-line tool to extract the corrupted archive ... and ... no CRC error is reported.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The archive should be reported as corrupted

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.zip.*;

public class Test {
	public static void main(String[] args) 
	{
    ZipEntry ze;

    try{
     String path_to_corrupted_jar = "x.jar";
     JarInputStream jis =
        new JarInputStream(new FileInputStream(path_to_corrupted_jar));
     byte buf[] = new byte[8000];
     while( (ze = jis.getNextEntry()) != null)
     {
       while(jis.read(buf)>0);
       System.out.println("Zip entry "+ze.getName()+" read OK");
     }
    } catch(ZipException ex) {
      /* Exception about CRC error should be thrown, but it is not */
      ex.printStackTrace();
    } catch(IOException ex) {
      ex.printStackTrace();
    }
	}
}
---------- END SOURCE ----------
(Review ID: 187744) 
======================================================================

Comments
EVALUATION We should compare CRCs for complete entries only. Found by David Bristor.
28-11-2006

SUGGESTED FIX --- ZipInputStream.java- 2006-11-14 16:37:38.000000000 +0300 +++ ZipInputStream.java 2006-11-30 19:05:24.000000000 +0300 @@ -158,10 +158,15 @@ if (len == -1) { throw new ZipException("unexpected EOF"); } crc.update(b, off, len); remaining -= len; + if (remaining == 0 && entry.crc != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } return len; default: throw new InternalError("invalid compression method"); } }
21-11-2006

EVALUATION The problem could be fixed adding explicit CRC check to the read(byte[], int, int) function into the STORED case. See Suggested Fix.
21-11-2006

EVALUATION Fixing 6332094 (use ZipFile instead of ZipInputStream) will alleviate the situation at least for the jar command, though not for the APIs.
10-10-2006

EVALUATION The zip/unzip command handles corrupted stored file contents correctly: $ echo data > file; rm -f my.zip; zip my.zip file; perl -pi -e 's/data/tada/' my.zip; unzip -o my.zip adding: file (stored 0%) Archive: my.zip extracting: file bad CRC 9a07f170 (should be e6c1c582) jar should implement the same algorithm. ###@###.### 2003-06-18
18-06-2003