Commit message: None
CVE: CVE-2017-13198
CWE: CWE-20
Vulnerability description: A vulnerability in the Android media framework (ex) related to composition of frames lacking a color map. Product: Android. Versions: 7.0, 7.1.1, 7.1.2, 8.0, 8.1. Android ID: A-68399117.

Project: Android

FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) :
    mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) {
}

Project: Android

FrameSequence_gif::FrameSequence_gif(Stream* stream) :
        mLoopCount(1), mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) {
    mGif = DGifOpen(stream, streamReader, NULL);
 if (!mGif) {
        ALOGW("Gif load failed");
 return;
 }

 if (DGifSlurp(mGif) != GIF_OK) {
        ALOGW("Gif slurp failed");
 DGifCloseFile(mGif, NULL);
        mGif = NULL;
 return;
 }

 long durationMs = 0;
 int lastUnclearedFrame = -1;
    mPreservedFrames = new bool[mGif->ImageCount];
    mRestoringFrames = new int[mGif->ImageCount];

 GraphicsControlBlock gcb;
 for (int i = 0; i < mGif->ImageCount; i++) {
 const SavedImage& image = mGif->SavedImages[i];

 for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) {
 ExtensionBlock* eb1 = image.ExtensionBlocks + j;
 ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1;
 if (eb1->Function == APPLICATION_EXT_FUNC_CODE
 && eb1->ByteCount == 11
 && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11)
 && eb2->Function == CONTINUE_EXT_FUNC_CODE
 && eb2->ByteCount == 3
 && eb2->Bytes[0] == 1) {
                mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]);
 }
 }

 DGifSavedExtensionToGCB(mGif, i, &gcb);

        durationMs += getDelayMs(gcb);

        mPreservedFrames[i] = false;
        mRestoringFrames[i] = -1;
 if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) {
            mPreservedFrames[lastUnclearedFrame] = true;
            mRestoringFrames[i] = lastUnclearedFrame;
 }
 if (!willBeCleared(gcb)) {
            lastUnclearedFrame = i;
 }
 }

#if GIF_DEBUG
    ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld",
            mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs);
 for (int i = 0; i < mGif->ImageCount; i++) {
 DGifSavedExtensionToGCB(mGif, i, &gcb);
        ALOGD("    Frame %d - must preserve %d, restore point %d, trans color %d",
                i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor);
 }
#endif

 if (mGif->SColorMap) {
 GraphicsControlBlock gcb;
 DGifSavedExtensionToGCB(mGif, 0, &gcb);
 if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) {
            mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]);
 }
 }
}

Project: Android

static bool acceptsBuffers() {
 return false;
}

Project: Android

static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) {
 return target.Left <= covered.Left
 && covered.Left + covered.Width <= target.Left + target.Width
 && target.Top <= covered.Top
 && covered.Top + covered.Height <= target.Top + target.Height;
}

Project: Android

static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap,
 int transparent, int width) {
 for (; width > 0; width--, src++, dst++) {
 if (*src != transparent && *src < cmap->ColorCount) {
 *dst = gifColorToColor8888(cmap->Colors[*src]);
 }
 }
}

Project: Android

static FrameSequence* createFramesequence(Stream* stream) {
 return new FrameSequence_gif(stream);
}

Project: Android

FrameSequenceState* FrameSequence_gif::createState() const {
 return new FrameSequenceState_gif(*this);
}

Project: Android

static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight,
 GifWord& copyWidth, GifWord& copyHeight) {
    copyWidth = imageDesc.Width;
 if (imageDesc.Left + copyWidth > maxWidth) {
        copyWidth = maxWidth - imageDesc.Left;
 }
    copyHeight = imageDesc.Height;
 if (imageDesc.Top + copyHeight > maxHeight) {
        copyHeight = maxHeight - imageDesc.Top;
 }
}

Project: Android

static long getDelayMs(GraphicsControlBlock& gcb) {
 return gcb.DelayTime * 10;
}

Project: Android

static Color8888 gifColorToColor8888(const GifColorType& color) {
 return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue);
}

Project: Android

static bool isGif(void* header, int header_size) {
 return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN)
 || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN)
 || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
}

Project: Android

void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) {
 const int width = mFrameSequence.getWidth();
 const int height = mFrameSequence.getHeight();
 if (!mPreserveBuffer) {
        ALOGD("preserve buffer not allocated! ah!");
 return;
 }
 for (int y = 0; y < height; y++) {
        memcpy(outputPtr + outputPixelStride * y,
                mPreserveBuffer + width * y,
                width * 4);
 }
}

Project: Android

void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) {
 if (frameNr == mPreserveBufferFrame) return;

    mPreserveBufferFrame = frameNr;
 const int width = mFrameSequence.getWidth();
 const int height = mFrameSequence.getHeight();
 if (!mPreserveBuffer) {
        mPreserveBuffer = new Color8888[width * height];
 }
 for (int y = 0; y < height; y++) {
        memcpy(mPreserveBuffer + width * y,
                outputPtr + outputPixelStride * y,
                width * 4);
 }
}

Project: Android

static void setLineColor(Color8888* dst, Color8888 color, int width) {
 for (; width > 0; width--, dst++) {
 *dst = color;
 }
}

Project: Android

static int streamReader(GifFileType* fileType, GifByteType* out, int size) {
 Stream* stream = (Stream*) fileType->UserData;
 return (int) stream->read(out, size);
}

Project: Android

static bool willBeCleared(const GraphicsControlBlock& gcb) {
 return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS;
}

Project: Android

FrameSequenceState_gif::~FrameSequenceState_gif() {
 delete[] mPreserveBuffer;
}