From 1684f4ee375db5ca3fd6c3388e743833faa4f063 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Mon, 10 Feb 2014 16:35:27 -0800 Subject: [PATCH] WebP Decoder: Mark some truncated bitstreams as invalid Specifically, check for truncated RIFF and/or VP8(L) chunks. For more context, see: https://code.google.com/p/webp/issues/detail?id=185 Change-Id: I91ca2dbf05080660fbc513244fc53adc57fc04b5 --- src/dec/idec.c | 1 + src/dec/webp.c | 26 +++++++++++++++++--------- src/dec/webpi.h | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/dec/idec.c b/src/dec/idec.c index 392f5d70..c9e9360a 100644 --- a/src/dec/idec.c +++ b/src/dec/idec.c @@ -308,6 +308,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { headers.data = data; headers.data_size = curr_size; + headers.have_all_data = 0; status = WebPParseHeaders(&headers); if (status == VP8_STATUS_NOT_ENOUGH_DATA) { return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. diff --git a/src/dec/webp.c b/src/dec/webp.c index 36c99dea..bf244839 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -52,13 +52,14 @@ static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) { } // Validates the RIFF container (if detected) and skips over it. -// If a RIFF container is detected, -// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and -// VP8_STATUS_OK otherwise. +// If a RIFF container is detected, returns: +// VP8_STATUS_BITSTREAM_ERROR for invalid header, +// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true, +// and VP8_STATUS_OK otherwise. // In case there are not enough bytes (partial RIFF container), return 0 for // *riff_size. Else return the RIFF size extracted from the header. static VP8StatusCode ParseRIFF(const uint8_t** const data, - size_t* const data_size, + size_t* const data_size, int have_all_data, size_t* const riff_size) { assert(data != NULL); assert(data_size != NULL); @@ -77,6 +78,9 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data, if (size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } + if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. + } // We have a RIFF container. Skip it. *riff_size = size; *data += RIFF_HEADER_SIZE; @@ -223,9 +227,8 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, // extracted from the VP8/VP8L chunk header. // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, - size_t* const data_size, - size_t riff_size, - size_t* const chunk_size, + size_t* const data_size, int have_all_data, + size_t riff_size, size_t* const chunk_size, int* const is_lossless) { const uint8_t* const data = *data_ptr; const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); @@ -248,6 +251,9 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) { return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. } + if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) { + return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream. + } // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. *chunk_size = size; *data_ptr += CHUNK_HEADER_SIZE; @@ -291,6 +297,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, int found_vp8x = 0; int animation_present = 0; int fragments_present = 0; + const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; VP8StatusCode status; WebPHeaderStructure hdrs; @@ -303,7 +310,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, hdrs.data_size = data_size; // Skip over RIFF header. - status = ParseRIFF(&data, &data_size, &hdrs.riff_size); + status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); if (status != VP8_STATUS_OK) { return status; // Wrong RIFF header / insufficient data. } @@ -353,7 +360,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, } // Skip over VP8/VP8L header. - status = ParseVP8Header(&data, &data_size, hdrs.riff_size, + status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, &hdrs.compressed_size, &hdrs.is_lossless); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. @@ -452,6 +459,7 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, headers.data = data; headers.data_size = data_size; + headers.have_all_data = 1; status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. if (status != VP8_STATUS_OK) { return status; diff --git a/src/dec/webpi.h b/src/dec/webpi.h index aa8088d4..457c72ed 100644 --- a/src/dec/webpi.h +++ b/src/dec/webpi.h @@ -54,6 +54,7 @@ void WebPResetDecParams(WebPDecParams* const params); typedef struct { const uint8_t* data; // input buffer size_t data_size; // input buffer size + int have_all_data; // true if all data is known to be available size_t offset; // offset to main data chunk (VP8 or VP8L) const uint8_t* alpha_data; // points to alpha chunk (if present) size_t alpha_data_size; // alpha chunk size