Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4040920
Votes 1
Synopsis Catch unexpected end of ZLIB while reading from InputStream for some ZIP files.
Category java:jar
Reported Against 1.1 , 2.0 , 1.1.2 , 1.2beta2
Release Fixed 1.2(1.2beta4)
State 11-Closed, Verified, bug
Priority: 3-Medium
Related Bugs 4103650 , 4156441 , 6519463
Submit Date 24-MAR-1997
Description
Exception only occurs for certain files in a ZIP file.
I found this out while decompressing the latest Java Tutorial (ZIP version). 
While decompressing it, 4 files throws exception:

	ui/drawing/images/MovingImage.gif
	images/HJLogo/T12.gif
	java/threads/example/RaceTest.class
	java/threads/example/BidirectionalBubbleSortAlgorithm.class

Even though the exception is thrown, I found out that the 4 files are OK. There 
seem to be no corruption at all since I compared this with the files that was
decompressed using the "unzip" program from /usr/dist. 
One of the file "MovingImage.gif" is compress in ZIP format in the attachment
"a.zip".

To reproduce this bug, get "a.zip" from attachment, compile the code below and
type "java unzip a.zip".

Error message for this bug is:

While reading: java.io.EOFException: Unexpected end of ZLIB input stream
java.io.EOFException: Unexpected end of ZLIB input stream
        at java.lang.Throwable.<init>(Compiled Code)
        at java.lang.Exception.<init>(Compiled Code)
        at java.io.IOException.<init>(Compiled Code)
        at java.io.EOFException.<init>(Compiled Code)
        at java.util.zip.InflaterInputStream.fill(Compiled Code)
        at java.util.zip.InflaterInputStream.read(Compiled Code)
        at java.io.FilterInputStream.read(Compiled Code)
        at unzip.main(Compiled Code)


==============
Source code is:

import java.lang.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

class unzip{
    public static void main(String[] args){
        if ( args.length < 1 ){
            System.out.println("Usage: java unzip <filename>");
            System.exit(1);
        }

        String filename=args[0];

        try{
            ZipFile fis = new ZipFile(filename);
            String line;
            byte buffer[] = new byte[1024];
            int length;

            System.out.println();

            //--- Creating files ---//
            for(Enumeration e=fis.entries(); e.hasMoreElements();){
                Object o=e.nextElement();
                line=o.toString();

                ZipEntry zentry=fis.getEntry(line);

                System.out.println("Inflating "+line+"...");
                InputStream inflate=fis.getInputStream(zentry);
                RandomAccessFile outfile=new RandomAccessFile(line,"rw");

                //-- Sometimes ZLIB unexpected EOF error occur.--//
                //-- We don't want to exit because of it. --//
                try{
                    while ( (length=inflate.read(buffer)) != -1){
                    outfile.write(buffer,0,length);
                    }
                } catch(IOException ex){
                    System.out.println("While reading: " + ex);
                    ex.printStackTrace();
                }

                outfile.close();
            }

            fis.close();
        } catch(FileNotFoundException e){
            System.err.println("Decompress: " + e);
        } catch(IOException e){
            System.err.println("Decompress: " + e);
        }
    }
}
=================================================================

This problem occurs 100% repeatably on two zip files that
I have, but some other files work okay.  I will send problem zip
files at your request.  The files were created by ZipIt on
the Mac, and can be deflated okay by unzip (PD version) on Win95
and by WinZip on Win95.

The output, including the stack trace, is:

+++ zip =   xxxxx@xxxxx  
+++ zip file entry 1 = ICONS/grain22.ico
+++      7358 characters
+++ zip file entry 2 = ICONS/grain12.ico
+++      7358 characters
+++ zip file entry 3 = ICONS/grain02.ico
java.io.EOFException: Unexpected end of ZLIB input stream
        at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:156)
        at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:119)
        at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:94)
        at JZip.main(JZip.java:20)


Here is the source program, which simply accesses every byte of
every entry in a zip file:

--------- JZip.java ---------------------------
import java.util.zip.*;
import java.util.*;
import java.io.*;

class JZip {

    public static void main(String[] args) {
	try {
	    ZipFile zip = new ZipFile(args[0]);
	    System.out.println("+++ zip = " + zip);
	    Enumeration entries = zip.entries();
	    int n = 0;
	    while (entries.hasMoreElements()) {
		ZipEntry entry = (ZipEntry)entries.nextElement();
		++n;
		System.out.println("+++ zip file entry " + n + " = " + entry);
		InputStream input = zip.getInputStream(entry);
		int c;
		int count = 0;
		while ((c = input.read()) >= 0)
		    ++count;  // System.out.print((char)c);
		System.out.println("+++      " + count + " characters");
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

}
-----------------------------
Work Around
The workaround is to never attempt to read more bytes than the entry contains. Call ZipEntry.getSize() to get the actual size of the entry, then use this value to keep track of the number of bytes remaining in the entry while reading from it. To take the previous example:

import java.lang.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

class unzip{
    public static void main(String[] args){
        if ( args.length < 1 ){
            System.out.println("Usage: java unzip <filename>");
            System.exit(1);
        }

        String filename=args[0];

        try{
            ZipFile fis = new ZipFile(filename);
            String line;
            byte buffer[] = new byte[1024];
            int length;

            System.out.println();

            //--- Creating files ---//
            for(Enumeration e=fis.entries(); e.hasMoreElements();){
                Object o=e.nextElement();
                line=o.toString();

                ZipEntry zentry=fis.getEntry(line);

                System.out.println("Inflating "+line+"...");
                InputStream inflate=fis.getInputStream(zentry);
                RandomAccessFile outfile=new RandomAccessFile(line,"rw");

                //-- Sometimes ZLIB unexpected EOF error occur.--//
                //-- We don't want to exit because of it. --//
                try{
		    int remaining = (int)zentry.getSize();
                    while (remaining>0 && (length=inflate.read(buffer, 0, Math.min(1024, remaining))) != -1){
			outfile.write(buffer,0,length);
			remaining -= length;
                    }
                } catch(IOException ex){
                    System.out.println("While reading: " + ex);
                    ex.printStackTrace();
                }

                outfile.close();
            }

            fis.close();
        } catch(FileNotFoundException e){
            System.err.println("Decompress: " + e);
        } catch(IOException e){
            System.err.println("Decompress: " + e);
        }
    }
}
Evaluation
There is a bug in ZipFile that causes InflaterInputStream to read beyond the end of the ZIP entry when attempting to read more bytes than the entry contains.
-- dac

This problem has been fixed in 1.2 but will probably not be fixed for 1.1.x.

  xxxxx@xxxxx   1998-04-22

Verified in jdk1.2fcsV build.
  xxxxx@xxxxx   1998-12-08
Comments
  
  Include a link with my name & email   

Submitted On 05-JAN-1998
dpz
Seems to be size related. I can make it happen
everytime with an entry of a particular size, but
if that entry is even one byte longer, it doesn't happen
size=656


Submitted On 22-JAN-1998
adam@organic.com
a class file size of 272 also seems to make it
break, maybe numbers which are palindromes in base
10 cause the problem :) ...this behavior appears
in jdk1.1.5


Submitted On 22-APR-1998
kiessig
Using ZipInputStream instead of
ZipFile also fixes this problem.
It seems to be slower only if you
don't want to read the whole file.


Submitted On 22-APR-1998
eanD
I have had simmilar trouble with InputStream but
I'm reading a HttpServletRequest stream. The read
never returns -1, so I have to work out how much to
read and just read that amount. I'm not shore if 
this is how HttpServletRequest is ment to work or
I there is an underling problem with InputStream.


Submitted On 14-MAY-1998
akj
The available method returns true when there are no
more bytes to read from an ZipInputStream. This is
probably because the InflaterInputStream reads 
beyond the end of the ZIP entry.


Submitted On 11-FEB-1999
gberche
The suggested work-around implies that ZipFile 
does not comply to the java.io.InputStream specs.
Reproduced here is the specs of InputStream.read()
 public abstract int read() throws IOException
    Reads the next byte of data from this input stream. The value byte is
returned as an int in the range 0 to 255. If no byte is available because the
end of
    the stream has been reached, the value -1 is returned. This method blocks
until input data is available, the end of the stream is detected, or an
exception
    is thrown. 
    A subclass must provide an implementation of this method. 
    Returns: 
        the next byte of data, or -1 if the end of the stream is reached. 
    Throws: IOException 
        if an I/O error occurs. 
When reaching the EOF, ZipInputStream throws a 
java.io.EOFException where it should simply
return the -1 value.
The exact bug is in InflaterInputStream.read(byte[] b, int off, int len)
which should catch EOFException from the InflaterInputStream.fill()
and then return the -1 value.
Following is a patch to the JDK 1.1.6 source ZipInputStream

    /**
     * Reads uncompressed data into an array of bytes. This method will
     * block until some input can be decompressed.
     * @param b the buffer into which the data is read
     * @param off the start offset of the data
     * @param len the maximum number of bytes read
     * @return the actual number of bytes read, or -1 if the end of the
     *         compressed input is reached or a preset dictionary is needed
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public int read(byte[] b, int off, int len) throws IOException {
	if (len &lt;= 0) {
	    return 0;
	}
	try {
	    int n;
	    while ((n = inf.inflate(b, off, len)) == 0) {
		if (inf.finished() || inf.needsDictionary()) {
		    return -1;
		}
		if (inf.needsInput()) {
		    fill();
		}
	    }
	    return n;
	} catch (DataFormatException e) {
	    String s = e.getMessage();
	    throw new ZipException(s != null ? s : &quot;Invalid ZLIB data
format&quot;);
//New block added
	} catch (EOFException e) {
	    //FIXME: Must also check ZipInputStream state to make sure
	    //it is in a consistent state in which incoming calls are
	    //properly handled!
	    return -1;
	}
//End of new block added
    }

The implications of this bug is that clients of ZipInputStream
can not use the ZipInputStream as a normal
InputStream object (for example passing it
to java.util.Properties.load()).
This is clearly against the object-oriented
concept of inheritance and encapsulation that
Java has strived to support
I believe that this bug should be fixed in the
1.1.x release!


Submitted On 18-FEB-1999
rossnelson
This bug is not completely fixed.  I can supply a file that exhibits the
problem under jdk1.2 final under Windows.


Submitted On 22-SEP-1999
mnmoore
I believe the analysis and patch suggested by gberche, above, is flawed (as is
the evaluation by Sun, but hey).  The EOFException should not be caught by the
read routine, because the EOFException should only be thrown when the inflater
suggests it's not &quot;finished&quot;, but there are no bytes left in the
underlying stream.  This is a valid error condition.
This is somewhat consistent with the primary use of EOFException (when
encountering EOF an while reading atomic data via DataInputStream), though you
could certainly argue that a ZipException is more in order.  I suspect that the
underlying native Inflater implementation is falsely reporting that it's not
done, when in fact it is.
The bizarre size sensitivity seems more likely to arise from a bug in the
actual inflater code than from some miscalculation in InputInflaterStream.  The
fact that the read data is apparently complete and accurate heightens my
suspicion.
If Sun &quot;fixed&quot; this bug for 1.2 based on the evaluation above, I can
see why rossnelson would suggest it's still happening...


Submitted On 12-JAN-2000
Parkway
Here's some code that fixes the problem. Modify java/util/zip/ZipFile.java as
follows:

Update one line in the function below:

    public InputStream getInputStream(ZipEntry ze) throws IOException {
        InputStream in = new ZipFileInputStream(this, ze);
        switch (ze.method) {
        case STORED:
            return in;
        case DEFLATED:
            // MODIFIED
            return new ZipFileInflaterInputStream(in, new Inflater(true),
ze.size);
        default:
            throw new ZipException(&quot;invalid compression method&quot;);
        }
    }

and add the following class:

class ZipFileInflaterInputStream extends InflaterInputStream {
    long size;
    ZipFileInflaterInputStream(InputStream in, Inflater inf, long size) {
        super(in, inf);
        this.size = size;
    }
    public int read(byte[] b, int off, int len) throws IOException {
        if (len &lt;= 0) return 0;
        if (size &lt;= 0) return -1;
        if (len &gt; size) len = (int)size;
        len = super.read(b, off, len);
        size -= len;
        return len;
    }
    public long skip(long n) throws IOException {
        if (n &gt; size) n = size;
        n = super.skip(n);
        size -= n;
        return n;
    }
    public int available() throws IOException {
        return (int)Math.min(size, Integer.MAX_VALUE);
    }
}


Submitted On 01-JUN-2001
sonykel
I'm still experiencing this problem under 1.3


Submitted On 23-OCT-2001
sgahan
It seems to be fixed again for 1.3.1



Submitted On 06-NOV-2001
jdbkkk
I'm getting this bug with 1.3.1-b24


Submitted On 12-MAR-2002
amswain
This is still a bug with JDK 1.3.1_01, fix it please !!


Submitted On 18-MAR-2002
MAF
I too am observing this bug under 1.3.1_01.
Please reopen this report and fix the bug.


Submitted On 19-JUN-2002
menep
This bug is still not fixed. I am experiencing it with the 
Jdk1.4 release version.


Submitted On 25-JUL-2002
atdance
It is july 2002 and I have  probelms with my
java version "1.3.1_04"

Can you fix it??


Submitted On 02-AUG-2002
raomara
I had the exception unexpected end of ZLIB while reading 
from a jarInputStream. I was advised to set the size of the 
jarentry at my server end, and use this value at the client 
end to only attempt to read in the correct number of bytes. 
However the size is set to -1 on my client side ??!!!. hence I 
have no way of separating this exception with more serious 
ones.


Submitted On 04-SEP-2002
jccampanero
Dear friends, I have found it in my jwsdp-1_0-ea2
installation, when starting the Tomcat server. I'm using
java se 1.4.0 over microsoft windows XP proffesional. I'll
try to resolve the problems with your comments.


Submitted On 03-FEB-2003
cssathya
I have been having the same problem in 1.4.01 under HP-UX 
(11i). The problem is exactly as described above. For some 
files, the same piece of code is working fine, but for others, it 
is not. Here is the code sample that reads the object.


READING THE OBJECT:

        public Object readObjectFromFile(String filename){
                Object object = null;
                try {
                        ObjectInputStream in = new 
ObjectInputStream(new GZIPInputStream(new FileInputStream
(filename)));
                        object = in.readObject();
                 }
        }

WRITING THE OBJECT:

        public static void writeObjectToFile(String filename, 
Object object){
                try {

                        ObjectOutputStream out = new 
ObjectOutputStream(new GZIPOutputStream(new 
FileOutputStream(filename)));
                        out.writeObject(object);
                        out.flush();
                        out.close();
                } catch (FileNotFoundException e){
                        System.out.println("Could find " + filename 
+ " on disk. Reason : " + e.getMessage());
                } catch (IOException e){
                        System.out.println("Could not write " + 
filename + " on to disk. Reason : " + e.getMessage());
                }
        }


Submitted On 22-JUL-2004
jamie0
Yup, it looks like this bug still occurs under JDK 1.4.2_02 on Mac OS X. It's too bad that this bug is closed or else somebody might realize that it needs fixing. Can't vote on it either. Can't fix the source and redistribute it either due to the not-really-open-source license for the JDK. The community has clearly fixed the bug and handed it back to Sun but Sun won't integrate the fixes.

I guess I'll have to code a workaround everywhere I see this problem. Yay!


Submitted On 31-AUG-2004
ramtin_shams
The problem is still happening with JRE 1.3.1_13, is someone from Sun going to attend to this?


Submitted On 07-OCT-2004
mytonytiger
This seems to still be an issue with 1.4.2_04 on Windows 2000. Why is it still marked closed?


Submitted On 29-AUG-2005
jessebee
I have experienced the same message, but it was due to an application that embeds recursively jar files with the same name except case sensivity
it works on unix stations but produces this message on MS-Windows (filenames no case sensitive)/ java 1.4.2 PCs


Submitted On 09-JAN-2008
This bug is now ten yeras old and marked fixed. It is not. The Inflater is still broken. In extensive tests I have demonstrated that the failure happens precisely when the input stream is not a stored block and ends with 0 or 1 unused bits.

In the failure mode the Inflater reads exactly four additional bytes from the input stream.



PLEASE NOTE: JDK6 is formerly known as Project Mustang