libtiff: WebP decoder: validate WebP blob width, height, band count against TIFF parameters

From 350ff161c8a61b6483a1e4689e09cd47dd0dd5f9 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Sat, 17 Jun 2023 16:22:38 +0200
Subject: [PATCH] WebP decoder: validate WebP blob width, height, band count
 against TIFF parameters

to avoid use of uninitialized variable, or decoding corrupted content
without explicit error

Fixes #581, fixes #582
---
 libtiff/tif_webp.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/libtiff/tif_webp.c b/libtiff/tif_webp.c
index 07db7cce..ce15391e 100644
--- a/libtiff/tif_webp.c
+++ b/libtiff/tif_webp.c
@@ -149,6 +149,57 @@ static int TWebPDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
                 segment_height = td->td_rowsperstrip;
         }
 
+        int webp_width, webp_height;
+        if (!WebPGetInfo(tif->tif_rawcp,
+                         tif->tif_rawcc > UINT32_MAX ? UINT32_MAX
+                                                     : (uint32_t)tif->tif_rawcc,
+                         &webp_width, &webp_height))
+        {
+            TIFFErrorExtR(tif, module, "WebPGetInfo() failed");
+            return 0;
+        }
+        if ((uint32_t)webp_width != segment_width ||
+            (uint32_t)webp_height != segment_height)
+        {
+            TIFFErrorExtR(
+                tif, module, "WebP blob dimension is %dx%d. Expected %ux%u",
+                webp_width, webp_height, segment_width, segment_height);
+            return 0;
+        }
+
+#if WEBP_DECODER_ABI_VERSION >= 0x0002
+        WebPDecoderConfig config;
+        if (!WebPInitDecoderConfig(&config))
+        {
+            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
+            return 0;
+        }
+
+        const bool bWebPGetFeaturesOK =
+            WebPGetFeatures(tif->tif_rawcp,
+                            tif->tif_rawcc > UINT32_MAX
+                                ? UINT32_MAX
+                                : (uint32_t)tif->tif_rawcc,
+                            &config.input) == VP8_STATUS_OK;
+
+        WebPFreeDecBuffer(&config.output);
+
+        if (!bWebPGetFeaturesOK)
+        {
+            TIFFErrorExtR(tif, module, "WebPInitDecoderConfig() failed");
+            return 0;
+        }
+
+        const int webp_bands = config.input.has_alpha ? 4 : 3;
+        if (webp_bands != sp->nSamples)
+        {
+            TIFFErrorExtR(tif, module,
+                          "WebP blob band count is %d. Expected %d", webp_bands,
+                          sp->nSamples);
+            return 0;
+        }
+#endif
+
         buffer_size = segment_width * segment_height * sp->nSamples;
         if (occ == (tmsize_t)buffer_size)
         {