JDK-7131823 : bug in GIFImageReader
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 7,8,17,18,19
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-01-20
  • Updated: 2023-01-11
  • Resolved: 2022-05-16
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.
JDK 11 JDK 13 JDK 15 JDK 17 JDK 19 JDK 8 Other
11.0.17Fixed 13.0.13Fixed 15.0.9Fixed 17.0.5Fixed 19 b23Fixed 8u371Fixed openjdk8u352Fixed
Description
FULL PRODUCT VERSION :
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
GIFImageReader can not read some GIF images,
because GIFImageReader has two bugs in LZW decoder.

1. LZW encoder can output the codes after the tree becomes full, without outputting the clearCode(for example it is 256, in 256-color gif image).

Good LZW encoder determines the timing of outputting the clearCode by the compression rate. if useing the old tree becomes good compression rate, Good LZW encoder does not output the clearCode. if not so, Good LZW encoder outputs the clearCode.

however, GIFImageReader is based on the wrong assumption, that is LZW encoder always outputs the  clearCode before tree becomes overflow.

when GIFImageReader reads a image which is created by Good LZW encoder, it causes ArrayIndexOutOfBoundsException.


2. textbookish LZW decoder is drop the first code. it does not attach to the tree.

however, GIFImageReader attachs first code to the tree. so tree is always broken.

I understand the part which should be corrected.
however I don't understand why GIFImageReader can read images with broken tree.

show other implementation of GIF decoder, you will be able to be judged whether I am right or wrong.

http://www.java2s.com/Code/Java/2D-Graphics-GUI/GiffileEncoder.htm
http://mxr.mozilla.org/mozilla-central/source/image/decoders/nsGIFDecoder2.cpp

both implementations assign  NullCode(-1) to oldCode at first, and they drop the first code.


paste part of corrected GIFImageReader#read(int, ImageReadParam)

            final int NULL_CODE = -1;
            int code, oldCode = NULL_CODE;

            int[] prefix = new int[4096];
            byte[] suffix = new byte[4096];
            byte[] initial = new byte[4096];
            int[] length = new int[4096];
            byte[] string = new byte[4096];

            initializeStringTable(prefix, suffix, initial, length);
            int tableIndex = (1 << initCodeSize) + 2;
            int codeSize = initCodeSize + 1;
            int codeMask = (1 << codeSize) - 1;

            while (!abortRequested()) {
                code = getCode(codeSize, codeMask);

                if (code == clearCode) {
                    initializeStringTable(prefix, suffix, initial, length);
                    tableIndex = (1 << initCodeSize) + 2;
                    codeSize = initCodeSize + 1;
                    codeMask = (1 << codeSize) - 1;

                    code = getCode(codeSize, codeMask);
                    oldCode = NULL_CODE;
                    if (code == eofCode) {
                        // Inform IIOReadProgressListeners of end of image
                        processImageComplete();
                        return theImage;
                    }
                } else if (code == eofCode) {
                    // Inform IIOReadProgressListeners of end of image
                    processImageComplete();
                    return theImage;
                } else {
                    int newSuffixIndex;
                    if (code < tableIndex) {
                        newSuffixIndex = code;
                    } else { // code == tableIndex
                        newSuffixIndex = oldCode;
                        if (code != tableIndex) {
                            // warning - code out of sequence
                            // possibly data corruption
                            processWarningOccurred("Out-of-sequence code!");
                        }
                    }

                    if (NULL_CODE != oldCode && tableIndex < 4096) {
                        int ti = tableIndex;
                        int oc = oldCode;

                        prefix[ti] = oc;
                        suffix[ti] = initial[newSuffixIndex];
                        initial[ti] = initial[oc];
                        length[ti] = length[oc] + 1;

                        ++tableIndex;
                        if ((tableIndex == (1 << codeSize)) &&
                            (tableIndex < 4096)) {
                            ++codeSize;
                            codeMask = (1 << codeSize) - 1;
                        }
                    }
                }

                // Reverse code
                int c = code;
                int len = length[c];
                for (int i = len - 1; i >= 0; i--) {
                    string[i] = suffix[c];
                    c = prefix[c];
                }

                outputPixels(string, len);
                oldCode = code;
            }



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I have some gif images which reproduce this bug.
I can mail it.


REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
case 1 no workaround.
case 2 use GifDecoder in
http://www.java2s.com/Code/Java/2D-Graphics-GUI/GiffileEncoder.htm

Comments
A pull request was submitted for review. URL: https://git.openjdk.org/jdk15u-dev/pull/259 Date: 2022-08-25 12:46:05 +0000
25-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk13u-dev/pull/393 Date: 2022-08-25 12:46:24 +0000
25-08-2022

Fix request (13u, 15u) I'd like to backport this fix here to be on par with major releases. Clean ports; jdk/javax/imageio tests pass.
25-08-2022

Fix request (8u) Patch applied with offsets after updating for the file paths. Similar to JDK17 and JDK11 backport request, as this is causing some issues due to the behavior changes introduced by JDK-8272462. New test, all jdk_imageio are passing. I currently have a handful of tier1 tests failing even without this change, so assuming they are unrelated.
16-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk8u-dev/pull/104 Date: 2022-08-16 21:08:33 +0000
16-08-2022

Fix request (11u) Clean backport Similar to JDK17 backport request, as this is causing some issues due to the behavior changes introduced by JDK-8272462. New test, all jdk_imageio, and tier1 are all passins.
05-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk11u-dev/pull/1317 Date: 2022-08-04 20:06:16 +0000
04-08-2022

All of the jdk_imageio tests, including the new one are passing on linux x64. The changes were adapted from a similar bug fix in OpenJFX a year ago and have been in tip for 3 months. We have also been running this change internally to Amazon on JDK8 and 11 for a few months with no reported issues. I feel this is low risk and has had a fair amount of bake time in other versions.
26-07-2022

HI @lutkerd Please elaborate on risk and testing a bit more. Did you run the enclosed test?
26-07-2022

Backport to JDK17 as this is causing some issues for our customers since JDK-8272462 caused a behavior change. This was also tested with a handful of images that caused the old AIOBE which now work fine.
25-07-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/549 Date: 2022-07-13 20:36:16 +0000
25-07-2022

Changeset: a31130fd Author: Dan Lutker <lutkerd@amazon.com> Committer: Paul Hohensee <phh@openjdk.org> Date: 2022-05-16 16:17:48 +0000 URL: https://git.openjdk.java.net/jdk/commit/a31130fd4056907edcb420761722c629a33273eb
16-05-2022

Assigning to me (phh) on behalf of Dan Lutker (lutkerd@amazon.com).
16-05-2022

A pull request was submitted for review. URL: https://git.openjdk.java.net/jdk/pull/8371 Date: 2022-04-22 20:04:32 +0000
22-04-2022

Please re-open if - if fix is in progress or on the plan to fix soon - if this is a P3 (file as P3, not P4)
14-03-2014