mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Perform additional APNG validations.
Special thank you to Stanislav Fort of Aisle Research (stanislav.fort@aisle.com) for finding this issue, bringing it to our attention, and offering a solution!
This commit is contained in:
@@ -20,6 +20,7 @@ import org.signal.glide.common.executor.FrameDecoderExecutor;
|
||||
import org.signal.glide.common.io.Reader;
|
||||
import org.signal.glide.common.io.Writer;
|
||||
import org.signal.glide.common.loader.Loader;
|
||||
import org.signal.glide.load.resource.apng.decode.APNGDecoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -238,9 +239,10 @@ public abstract class FrameSeqDecoder<R extends Reader, W extends Writer> {
|
||||
return fullRect;
|
||||
}
|
||||
|
||||
private void initCanvasBounds(Rect rect) {
|
||||
private void initCanvasBounds(Rect rect) throws IOException {
|
||||
fullRect = rect;
|
||||
frameBuffer = ByteBuffer.allocate((rect.width() * rect.height() / (sampleSize * sampleSize) + 1) * 4);
|
||||
int capacity = APNGDecoder.getSafeAllocationSize(fullRect.width(), fullRect.height(), sampleSize);
|
||||
frameBuffer = ByteBuffer.allocate(capacity);
|
||||
if (mWriter == null) {
|
||||
mWriter = getWriter();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ public class APNGDecoder extends FrameSeqDecoder<APNGReader, APNGWriter> {
|
||||
private int mLoopCount;
|
||||
private final Paint paint = new Paint();
|
||||
|
||||
public static final int MAX_DIMENSION = 4096;
|
||||
public static final long MAX_TOTAL_PIXELS = 64_000_000L;
|
||||
|
||||
private class SnapShot {
|
||||
byte dispose_op;
|
||||
@@ -126,8 +128,12 @@ public class APNGDecoder extends FrameSeqDecoder<APNGReader, APNGWriter> {
|
||||
otherChunks.add(chunk);
|
||||
}
|
||||
}
|
||||
frameBuffer = ByteBuffer.allocate((canvasWidth * canvasHeight / (sampleSize * sampleSize) + 1) * 4);
|
||||
snapShot.byteBuffer = ByteBuffer.allocate((canvasWidth * canvasHeight / (sampleSize * sampleSize) + 1) * 4);
|
||||
|
||||
int capacity = getSafeAllocationSize(canvasWidth, canvasHeight, sampleSize);
|
||||
|
||||
frameBuffer = ByteBuffer.allocate(capacity);
|
||||
snapShot.byteBuffer = ByteBuffer.allocate(capacity);
|
||||
|
||||
return new Rect(0, 0, canvasWidth, canvasHeight);
|
||||
}
|
||||
|
||||
@@ -208,4 +214,27 @@ public class APNGDecoder extends FrameSeqDecoder<APNGReader, APNGWriter> {
|
||||
Log.e(TAG, "Failed to render!", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getSafeAllocationSize(int width, int height, int sampleSize) throws IOException {
|
||||
if (width <= 0 || height <= 0 || width > MAX_DIMENSION || height > MAX_DIMENSION) {
|
||||
throw new IOException("APNG dimensions exceed safe limits: " + width + "x" + height);
|
||||
}
|
||||
|
||||
int capacity;
|
||||
try {
|
||||
int ss = Math.multiplyExact(sampleSize, sampleSize);
|
||||
int canvasSize = Math.multiplyExact(width, height);
|
||||
|
||||
int pixelCount = canvasSize / ss + 1;
|
||||
if (pixelCount <= 0 || pixelCount > MAX_TOTAL_PIXELS) {
|
||||
throw new IOException("APNG pixel count exceeds safe limits: " + pixelCount);
|
||||
}
|
||||
|
||||
capacity = Math.multiplyExact(pixelCount, 4);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new IOException("Failed to multiply dimensions and sample size: " + width + "x" + height + " @ sample size " + sampleSize + " (overflow?)", e);
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user