* A security researcher crafted a gif that would cause the Android Bitmap code to throw an NPE. That would cause messaging to crash when decoding the NPE. The frameworks team is changing the underlying code to throw an OutOfMemoryError instead of a NullPointerException. In order to catch both errors, the code needs to catch Throwable. Test: I added code to GifImageResource.getDrawable to throw a new OutOfMemoryError and then used the debugger to verify it was caught by the new catch Throwable statement. I did the same test with NullPointerException. I tested attaching gif images and sending them to verify the gif path still worked. BUG=37742976 Change-Id: If71a7e65f8c0b083fe6c4b79f78358666338d59d
119 lines
3.6 KiB
Java
119 lines
3.6 KiB
Java
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.messaging.datamodel.media;
|
|
|
|
import android.content.res.Resources;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.media.ExifInterface;
|
|
import android.support.rastermill.FrameSequence;
|
|
import android.support.rastermill.FrameSequenceDrawable;
|
|
|
|
import com.android.messaging.util.Assert;
|
|
import com.android.messaging.util.LogUtil;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
public class GifImageResource extends ImageResource {
|
|
private FrameSequence mFrameSequence;
|
|
|
|
public GifImageResource(String key, FrameSequence frameSequence) {
|
|
// GIF does not support exif tags
|
|
super(key, ExifInterface.ORIENTATION_NORMAL);
|
|
mFrameSequence = frameSequence;
|
|
}
|
|
|
|
public static GifImageResource createGifImageResource(String key, InputStream inputStream) {
|
|
final FrameSequence frameSequence;
|
|
try {
|
|
frameSequence = FrameSequence.decodeStream(inputStream);
|
|
} finally {
|
|
try {
|
|
inputStream.close();
|
|
} catch (IOException e) {
|
|
// Nothing to do if we fail closing the stream
|
|
}
|
|
}
|
|
if (frameSequence == null) {
|
|
return null;
|
|
}
|
|
return new GifImageResource(key, frameSequence);
|
|
}
|
|
|
|
@Override
|
|
public Drawable getDrawable(Resources resources) {
|
|
try {
|
|
return new FrameSequenceDrawable(mFrameSequence);
|
|
} catch (final Throwable t) {
|
|
// Malicious gif images can make the platform throw different kind of throwables, such
|
|
// as OutOfMemoryError and NullPointerException. Catch them all.
|
|
LogUtil.e(LogUtil.BUGLE_TAG, "Error getting drawable for GIF", t);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Bitmap getBitmap() {
|
|
Assert.fail("GetBitmap() should never be called on a gif.");
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public byte[] getBytes() {
|
|
Assert.fail("GetBytes() should never be called on a gif.");
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Bitmap reuseBitmap() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean supportsBitmapReuse() {
|
|
// FrameSequenceDrawable a.) takes two bitmaps and thus does not fit into the current
|
|
// bitmap pool architecture b.) will rarely use bitmaps from one FrameSequenceDrawable to
|
|
// the next that are the same sizes since they are used by attachments.
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int getMediaSize() {
|
|
Assert.fail("GifImageResource should not be used by a media cache");
|
|
// Only used by the media cache, which this does not use.
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean isCacheable() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void close() {
|
|
acquireLock();
|
|
try {
|
|
if (mFrameSequence != null) {
|
|
mFrameSequence = null;
|
|
}
|
|
} finally {
|
|
releaseLock();
|
|
}
|
|
}
|
|
|
|
}
|