I. BACKGROUND

http://en.wikipedia.org/wiki/JavaSE
http://openjdk.java.net/projects/jdk7u/
http://hg.openjdk.java.net/jdk7u/jdk7u-dev/jdk  // Java and native sources


II. DESCRIPTION

The invalid array indexing vulnerability exists within the native 
storeImageArray() function inside jre/bin/awt.dll

    // see ...\jdk\src\share\native\sun\awt\image\awt_parseImage.c

    static int storeImageArray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP, mlib_image *mlibImP) 
    {
        int mStride;
        unsigned char *cmDataP, *dataP, *cDataP;
        HintS_t *hintP = &dstP->hints;
        RasterS_t *rasterP = &dstP->raster;
        int y;      
        ...
        if (hintP->packing == BYTE_INTERLEAVED) {
            /* Write it back to the destination */
            cmDataP = (unsigned char *) mlib_ImageGetData(mlibImP);
            mStride = mlib_ImageGetStride(mlibImP);
            dataP = (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, rasterP->jdata, NULL);
            if (dataP == NULL) return 0;
            
            cDataP = dataP + hintP->dataOffset;
            for (y=0; y < rasterP->height;
                 y++, cmDataP += mStride, cDataP += hintP->sStride)
            {
                memcpy(cDataP, cmDataP, rasterP->width*hintP->numChans);
            }
        }
        ...
    }

The "dataP" pointer can be taken from "data" field of the 
"sun.awt.image.BytePackedRaster" Java object. 
The "hintP->dataOffset" value is taken from the "dataBitOffset" field.  

BytePackedRaster is not accessible directly, but can be created via the public 
java.awt.image.Raster.createWritableRaster() method: 

    // http://docs.oracle.com/javase/7/docs/api/java/awt/image/Raster.html#createWritableRaster(java.awt.image.SampleModel, java.awt.image.DataBuffer, java.awt.Point)
    public static WritableRaster createWritableRaster(
        SampleModel sm, 
        DataBuffer db, 
        Point location
    );

The first parameter of "createWritableRaster()" is "java.awt.image.SampleModel".  
The sample model object should be java.awt.image.MultiPixelPackedSampleModel in 
order to create BytePackedRaster.  

    // http://docs.oracle.com/javase/7/docs/api/java/awt/image/MultiPixelPackedSampleModel.html#MultiPixelPackedSampleModel(int, int, int, int, int, int)
    public MultiPixelPackedSampleModel(
        int dataType,
        int w,
        int h,
        int numberOfBits,
        int scanlineStride,
        int dataBitOffset
    );

MultiPixelPackedSampleModel accepts dataBitOffset parameter w/o any checks.
BytePackedRaster() constructor calculates the dataBitOffset value basing on
SampleModel and DataBuffer parameters:

    // see ...\jdk\src\share\classes\sun\awt\image\BytePackedRaster.java
    public BytePackedRaster(SampleModel sampleModel,
                            DataBuffer dataBuffer,
                            Rectangle aRegion,
                            Point origin,
                            BytePackedRaster parent){
        super(sampleModel,dataBuffer,aRegion,origin, parent);
        this.maxX = minX + width;
        this.maxY = minY + height;

        if (!(dataBuffer instanceof DataBufferByte)) {
           throw new RasterFormatException("BytePackedRasters must have" +
                "byte DataBuffers");
        }
        DataBufferByte dbb = (DataBufferByte)dataBuffer;
        this.data = stealData(dbb, 0);
        if (dbb.getNumBanks() != 1) {
            throw new
                RasterFormatException("DataBuffer for BytePackedRasters"+
                                      " must only have 1 bank.");
        }
        int dbOffset = dbb.getOffset();

        if (sampleModel instanceof MultiPixelPackedSampleModel) {
            MultiPixelPackedSampleModel mppsm =
                (MultiPixelPackedSampleModel)sampleModel;
            this.type = IntegerComponentRaster.TYPE_BYTE_BINARY_SAMPLES;
            pixelBitStride = mppsm.getPixelBitStride();
            if (pixelBitStride != 1 &&
                pixelBitStride != 2 &&
                pixelBitStride != 4) {
                throw new RasterFormatException
                  ("BytePackedRasters must have a bit depth of 1, 2, or 4");
            }
            scanlineStride = mppsm.getScanlineStride();
            dataBitOffset = mppsm.getDataBitOffset() + dbOffset*8;
            int xOffset = aRegion.x - origin.x;
            int yOffset = aRegion.y - origin.y;
            dataBitOffset += xOffset*pixelBitStride + yOffset*scanlineStride*8;
            bitMask = (1 << pixelBitStride) -1;
            shiftOffset = 8 - pixelBitStride;
        } else {
            throw new RasterFormatException("BytePackedRasters must have"+
                "MultiPixelPackedSampleModel");
        }
        verify(false);
    }

and tries to validate dataBitOffset using the following code:

    private void verify (boolean strictCheck) {
        // Make sure data for Raster is in a legal range
        if (dataBitOffset < 0) {
            throw new RasterFormatException("Data offsets must be >= 0");
        }

        int lastbit = (dataBitOffset
                       + (height-1) * scanlineStride * 8
                       + (width-1) * pixelBitStride
                       + pixelBitStride - 1);
        if (lastbit / 8 >= data.length) {
            throw new RasterFormatException("raster dimensions overflow " +
                                            "array bounds");
        }
        if (strictCheck) {
            if (height > 1) {
                lastbit = width * pixelBitStride - 1;
                if (lastbit / 8 >= scanlineStride) {
                    throw new RasterFormatException("data for adjacent" +
                                                    " scanlines overlaps");
                }
            }
        }
    }

Even if you don't see multiply signed integer overflows inside both 
BytePackedRaster() and verify(), or if you don't know how to use integer 
overflows, even in these cases verify() allows you to set "dataBitOffset" 
almost eight times bigger than "data.length". So, "dataBitOffset" can point 
outside the "data" array and this is enough to cause the memory corruption 
inside the native storeImageArray() function.

Normally, memory corruption doesn't occur for BytePackedRaster because there is
"if (hintP->packing == BYTE_INTERLEAVED)" condition inside storeImageArray().
Fortunately it's possible to bypass it by forcing Java to handle byte packed 
rasters as interleaved rasters using the custom ColorModel object. See the 
exploit sources for details.




III. ANALYSIS

This vulnerability allows remote attackers to execute arbitrary code on vulnerable 
installations of Oracle Java. User interaction is required to exploit this 
vulnerability in that the target must visit a malicious page or open a malicious file.



IV. DETECTION
Oracle Java SE 6,7
