JDK-4977007 : JPEG Image Decoding questionable as a result of fixes for bugs 4836529, 4903659
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2004-01-12
  • Updated: 2004-02-09
  • Resolved: 2004-02-09
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
5.0 b38Fixed
Related Reports
Relates :  
Description

Name: tb29552			Date: 01/12/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b31)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b31, mixed mode)

I also took the source code changes for bugs 4836529 and 4903659 and back
ported them to the 1.4 source for the jpegimagedecoderimpl, and both my version of the 1.4 libjpeg.so and the 1.5.0-beta-b31 behaved the same way.

ADDITIONAL OS VERSION INFORMATION :
Linux 2.4.7-10smp #1 SMP Thu Sep 6 16:16:16 EDT 2001 i686 unknown

I believe this problem is actually OS independent, since it's in the JPEG native library implementation.

A DESCRIPTION OF THE PROBLEM :
NOTE:  I'm a Sun employee, Professional Services IT Architect working
onsite at Network Solutions in Herndon, Virginia.  Although I've tested
against 1.5.0, the customer does not have access to this beta distribution.

The fix for bug 4836529 properly prevents the JVM from crashing when
reading a corrupted JPEG image.  However, the fix for 4903659 seems to
prevent certain seemingly-valid JPEG images from being decoded as well,
while definitely preventing the fatal allocation-loop described in the original
bug report.

I can provide test images which demonstrate this problem;  please email
me at either ###@###.### or ###@###.###.
I have three images:

   - corrupt.jpg -- proves that 4903659 is fixed;
                             No more endless looping until an OutOfMemory crash.  The
                             decode completes without an exception thrown, which is
                             consistent with other non-Java JPEG viewers, such as
                             mozilla.

   - Kaat_corrupt.jpg -- proves that 4836529 is fixed;
                              No more JVM crashes, just a friendly ImageFormatException.
                              This file is a truly bad image, and cannot be opened by
                              mozilla or other image viewers.

   - Kaat_good.jpg -- the problem image now that both fixes for bugs 4836529
                               and 4903659 are in place;  it throws an
                               ImageFormatException in 1.5.0 and when I backported the
                               fixes to the 1.4 open source.  Without the fixes in place, this
                               image decodes without an error in JDK 1.[234].  It also
                               opens fine in other non-Java image viewers.

If I change the code fix for bug 4903659 from:

jpegimagedecoderimpl.c:

572           // check for JPEG_EOI and reading errors
573           if (src->pub.next_input_byte[len-1] == JPEG_EOI
574               || src->err != NULL)
575           {
576               // no more data in the buffer, leave loop.
577               break;
578           }

to this:

572           // check for reading errors
573           if (src->err != NULL)
575           {
576               // there's an error, stop reading input.
577               break;
578           }

then all three images are decoded the way I'd expect them to.  That is,

-  corrupt.jpg no longer loops until an OutOfMemory and doesn't throw an
ImageFormatException,

-  Kaat_corrupt.jpg throws an ImageFormatException

-  Kaat_good.jpg decodes with no errors.

I'm not sure why exactly checking for JPEG_EOI isn't a good thing for
Kaat_good.jpg, but without that check, the behavior seems more correct.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See description.  Email me for the test images.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The JVM I'm using is on Linux 2.4.7-10smp:

bash-2.05$ /opt/jdk/1.4.1_01-jpegfix/bin/java -version
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

except that I've replaced the libjpeg.so with a custom built version of the 1.4
libjpeg.so with the fixes for 4836529 and 4903659 backported from tiger,
and with the fix for 4903659 changed to *not* check for JPEG_EOI, just
that there are no input errors.

bash-2.05$ /opt/jdk/1.4.1_01-jpegfix/bin/java -classpath . JpegTest corrupt.jpg
Corrupt JPEG data: 1 extraneous bytes before marker 0xeb
[returns to Unix prompt without an exception or crash -- this is good]

bash-2.05$ /opt/jdk/1.4.1_01-jpegfix/bin/java -classpath . JpegTest Kaat_corrupt.jpg
Corrupt JPEG data: 325 extraneous bytes before marker 0xd8
com.sun.image.codec.jpeg.ImageFormatException: Invalid JPEG file structure: two SOI markers
        at sun.awt.image.codec.JPEGImageDecoderImpl.readJPEGStream(Native Method)
        at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(JPEGImageDecoderImpl.java:210)
        at JpegTest.main(JpegTest.java:29)
[this is excepted as a result of the bug fix, and is consistent with errors given
by other non-Java image viewers]

bash-2.05$ /opt/jdk/1.4.1_01-jpegfix/bin/java -classpath . JpegTest Kaat_good.jpg
[returns to Unix prompt without an exception thrown -- consistent with the
ability of other non-Java image viewers to open Kaat_good.jpg]

ACTUAL -
The JVM I'm using is on Linux 2.4.7-10smp:

bash-2.05$ /opt/jdk/1.5.0/bin/java -version
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b31)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b31, mixed mode)

bash-2.05$ /opt/jdk/1.5.0/bin/java -classpath . JpegTest corrupt.jpg
Corrupt JPEG data: 1 extraneous bytes before marker 0xeb
[returns to Unix prompt  without an exception or crash -- this is good.]

bash-2.05$ /opt/jdk/1.5.0/bin/java -classpath . JpegTest Kaat_corrupt.jpg
Corrupt JPEG data: 325 extraneous bytes before marker 0xd8
com.sun.image.codec.jpeg.ImageFormatException: Invalid JPEG file structure: two SOI markers
        at sun.awt.image.codec.JPEGImageDecoderImpl.readJPEGStream(Native Method)
        at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(JPEGImageDecoderImpl.java:210)
        at JpegTest.main(JpegTest.java:29)
[this is expected as a result of the bug fix, and is consistent with errors given
by other non-Java image viewers]

bash-2.05$ /opt/jdk/1.5.0/bin/java -classpath . JpegTest Kaat_good.jpg
Corrupt JPEG data: 307 extraneous bytes before marker 0xd8
com.sun.image.codec.jpeg.ImageFormatException: Invalid JPEG file structure: two SOI markers
        at sun.awt.image.codec.JPEGImageDecoderImpl.readJPEGStream(Native Method)
        at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(JPEGImageDecoderImpl.java:210)
        at JpegTest.main(JpegTest.java:29)
[this is the problem -- this is not consistent with other non-Java image viewers
which are able to decode Kaat_good.jpg without an error]

ERROR MESSAGES/STACK TRACES THAT OCCUR :
See actual results detail.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
This is the same JpegTest class referenced in the detail for bug 4903659.

import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.image.codec.jpeg.TruncatedFileException;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;

public class JpegTest
{
    public static void main(String[] args)
    {
        ByteArrayInputStream is = null;

        try
        {
            File sourceFile = new File(args[0]);
            byte[] data = new byte[(int)sourceFile.length()];
            RandomAccessFile f = new RandomAccessFile(sourceFile, "r");
            f.readFully(data);
            f.close();
            is = new ByteArrayInputStream(data);
            JPEGImageDecoder dec = JPEGCodec.createJPEGDecoder(is);
            BufferedImage bufferedImage = dec.decodeAsBufferedImage();
        }
        catch(IOException io)
        {
            io.printStackTrace();
        }
        catch( Throwable t)
        {
            t.printStackTrace();
        }
        finally
        {
            if(is != null)
            {
                try
                {
                    is.close();
                }
                catch(IOException io)
                {}
            }
        }
    }
    
}



---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Modification to code fix for bug 4903659 in jpegimagedecoderimpl.c.
See description.
(Incident Review ID: 233149) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta2 FIXED IN: tiger-beta2 INTEGRATED IN: tiger-b38 tiger-beta2
21-08-2004

EVALUATION Name: abR10136 Date: 01/19/2004 The problem here is what we break the reading loop if the last marker data element is equal to 0xd9 (JPEG_EOI). This is not correct because according to JPEG specs (http://www.w3.org/Graphics/JPEG/jfif3.pdf) we can legally encounter 0xd9 anywhere in the data (data segment length is specified explicitly). So, we should break the reading loop only if we get an error (as it is proposed in the suggested fix). ======================================================================
21-08-2004