From d45f80f25d99b1aa7c7a3175a5c1853d9b07bb34 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 26 Mar 2026 15:39:03 -0400 Subject: [PATCH] Improve APNG validation in new APNG renderer. --- .../src/main/java/org/signal/apng/ApngDecoder.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/apng/src/main/java/org/signal/apng/ApngDecoder.kt b/lib/apng/src/main/java/org/signal/apng/ApngDecoder.kt index fa7578d423..dbad367e45 100644 --- a/lib/apng/src/main/java/org/signal/apng/ApngDecoder.kt +++ b/lib/apng/src/main/java/org/signal/apng/ApngDecoder.kt @@ -46,6 +46,7 @@ class ApngDecoder private constructor( companion object { private val PNG_MAGIC = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A) + private const val MAX_DIMENSION: UInt = 4096u @Throws(IOException::class) fun isApng(inputStream: InputStream): Boolean { @@ -219,7 +220,7 @@ class ApngDecoder private constructor( chunkType = scanner.readBytes(4).toString(Charsets.US_ASCII) } - if (earlyFctl != null) { + if (earlyFctl != null && isValidFrame(earlyFctl, ihdr)) { frames += Frame(fcTL = earlyFctl, dataRegions = idatRegions, isIdat = true) } @@ -258,7 +259,7 @@ class ApngDecoder private constructor( chunkType = scanner.readBytes(4).toString(Charsets.US_ASCII) } - if (fdatRegions.isNotEmpty()) { + if (fdatRegions.isNotEmpty() && isValidFrame(fctl, ihdr)) { frames += Frame(fcTL = fctl, dataRegions = fdatRegions, isIdat = false) } } @@ -272,6 +273,13 @@ class ApngDecoder private constructor( ) } + private fun isValidFrame(fctl: Chunk.fcTL, ihdr: Chunk.IHDR): Boolean { + return fctl.width in 1u..MAX_DIMENSION && + fctl.height in 1u..MAX_DIMENSION && + fctl.xOffset + fctl.width <= ihdr.width && + fctl.yOffset + fctl.height <= ihdr.height + } + private fun parseFctl(data: ByteArray): Chunk.fcTL { return Chunk.fcTL( sequenceNumber = data.sliceArray(0 until 4).toUInt(),