From 3fab596c14da75b6800448c859cba849d8724dd5 Mon Sep 17 00:00:00 2001 From: mundur Date: Tue, 28 Jul 2026 19:28:34 +0800 Subject: [PATCH] Tighten decoder image/alloc caps and fail closed on Huffman group amplification. Align MAX_IMAGE_AREA with the encoder WEBP_MAX_DIMENSION limit, lower the default WEBP_MAX_ALLOCABLE_MEMORY ceiling from 16GiB to 1GiB, and reject lossless meta-Huffman bitstreams that advertise more than 1000 groups instead of still parsing unused tables. --- src/dec/vp8l_dec.c | 36 ++++++++++-------------------------- src/utils/utils.h | 4 +++- src/webp/format_constants.h | 6 ++++-- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/dec/vp8l_dec.c b/src/dec/vp8l_dec.c index c112b300e..bf6c6acb9 100644 --- a/src/dec/vp8l_dec.c +++ b/src/dec/vp8l_dec.c @@ -394,33 +394,17 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, num_htree_groups_max = group + 1; } } - // Check the validity of num_htree_groups_max. If it seems too big, use a - // smaller value for later. This will prevent big memory allocations to end - // up with a bad bitstream anyway. - // The value of 1000 is totally arbitrary. We know that num_htree_groups_max - // is smaller than (1 << 16) and should be smaller than the number of pixels - // (though the format allows it to be bigger). - if (num_htree_groups_max > 1000 || num_htree_groups_max > xsize * ysize) { - // Create a mapping from the used indices to the minimal set of used - // values [0, num_htree_groups) - mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping)); - if (mapping == NULL) { - VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); - goto Error; - } - // -1 means a value is unmapped, and therefore unused in the Huffman - // image. - WEBP_UNSAFE_MEMSET(mapping, 0xff, - num_htree_groups_max * sizeof(*mapping)); - for (num_htree_groups = 0, i = 0; i < huffman_pixs; ++i) { - // Get the current mapping for the group and remap the Huffman image. - int* const mapped_group = &mapping[huffman_image[i]]; - if (*mapped_group == -1) *mapped_group = num_htree_groups++; - huffman_image[i] = *mapped_group; - } - } else { - num_htree_groups = num_htree_groups_max; + // Check the validity of num_htree_groups_max. Sparse high group IDs would + // otherwise force parsing up to 65k Huffman tables (amplification). Fail + // closed instead of remapping and still walking the full index range. + // The value of 1000 matches the historical soft threshold and stays below + // the pixel count when the image is small. + if (num_htree_groups_max > 1000 || + num_htree_groups_max > (uint64_t)xsize * ysize) { + VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); + goto Error; } + num_htree_groups = num_htree_groups_max; } if (br->eos) goto Error; diff --git a/src/utils/utils.h b/src/utils/utils.h index 9f6b252e8..c706cfacc 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -37,7 +37,9 @@ extern "C" { // This is the maximum memory amount that libwebp will ever try to allocate. #ifndef WEBP_MAX_ALLOCABLE_MEMORY #if SIZE_MAX > (1ULL << 34) -#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 34) +/* Cap single allocations to 1 GiB by default (was 16 GiB). Override at + * compile time if a larger budget is intentionally required. */ +#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 30) #else // For 32-bit targets keep this below INT_MAX to avoid valgrind warnings. #define WEBP_MAX_ALLOCABLE_MEMORY ((1ULL << 31) - (1 << 16)) diff --git a/src/webp/format_constants.h b/src/webp/format_constants.h index 3655a12ef..53c6d26b0 100644 --- a/src/webp/format_constants.h +++ b/src/webp/format_constants.h @@ -78,8 +78,10 @@ typedef enum { #define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. -#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. -#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. +#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. +/* Align decoder area with encoder WEBP_MAX_DIMENSION (16383) to prevent + * multi-GiB canvas allocations from crafted VP8X dimensions. */ +#define MAX_IMAGE_AREA (16383ULL * 16383ULL) #define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count #define MAX_DURATION (1 << 24) // maximum duration #define MAX_POSITION_OFFSET (1 << 24) // maximum frame x/y offset