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
This commit is contained in:
Urvang Joshi 2014-02-10 16:35:27 -08:00
parent 393f89b763
commit 1684f4ee37
3 changed files with 19 additions and 9 deletions

View File

@ -308,6 +308,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
headers.data = data; headers.data = data;
headers.data_size = curr_size; headers.data_size = curr_size;
headers.have_all_data = 0;
status = WebPParseHeaders(&headers); status = WebPParseHeaders(&headers);
if (status == VP8_STATUS_NOT_ENOUGH_DATA) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.

View File

@ -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. // Validates the RIFF container (if detected) and skips over it.
// If a RIFF container is detected, // If a RIFF container is detected, returns:
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and // VP8_STATUS_BITSTREAM_ERROR for invalid header,
// VP8_STATUS_OK otherwise. // 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 // In case there are not enough bytes (partial RIFF container), return 0 for
// *riff_size. Else return the RIFF size extracted from the header. // *riff_size. Else return the RIFF size extracted from the header.
static VP8StatusCode ParseRIFF(const uint8_t** const data, 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) { size_t* const riff_size) {
assert(data != NULL); assert(data != NULL);
assert(data_size != NULL); assert(data_size != NULL);
@ -77,6 +78,9 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
if (size > MAX_CHUNK_PAYLOAD) { if (size > MAX_CHUNK_PAYLOAD) {
return VP8_STATUS_BITSTREAM_ERROR; 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. // We have a RIFF container. Skip it.
*riff_size = size; *riff_size = size;
*data += RIFF_HEADER_SIZE; *data += RIFF_HEADER_SIZE;
@ -223,9 +227,8 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
// extracted from the VP8/VP8L chunk header. // extracted from the VP8/VP8L chunk header.
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. // 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, static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
size_t* const data_size, size_t* const data_size, int have_all_data,
size_t riff_size, size_t riff_size, size_t* const chunk_size,
size_t* const chunk_size,
int* const is_lossless) { int* const is_lossless) {
const uint8_t* const data = *data_ptr; const uint8_t* const data = *data_ptr;
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); 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)) { if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information. 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. // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
*chunk_size = size; *chunk_size = size;
*data_ptr += CHUNK_HEADER_SIZE; *data_ptr += CHUNK_HEADER_SIZE;
@ -291,6 +297,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
int found_vp8x = 0; int found_vp8x = 0;
int animation_present = 0; int animation_present = 0;
int fragments_present = 0; int fragments_present = 0;
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
VP8StatusCode status; VP8StatusCode status;
WebPHeaderStructure hdrs; WebPHeaderStructure hdrs;
@ -303,7 +310,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
hdrs.data_size = data_size; hdrs.data_size = data_size;
// Skip over RIFF header. // 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) { if (status != VP8_STATUS_OK) {
return status; // Wrong RIFF header / insufficient data. return status; // Wrong RIFF header / insufficient data.
} }
@ -353,7 +360,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
} }
// Skip over VP8/VP8L header. // 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); &hdrs.compressed_size, &hdrs.is_lossless);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. 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 = data;
headers.data_size = data_size; headers.data_size = data_size;
headers.have_all_data = 1;
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks. status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; return status;

View File

@ -54,6 +54,7 @@ void WebPResetDecParams(WebPDecParams* const params);
typedef struct { typedef struct {
const uint8_t* data; // input buffer const uint8_t* data; // input buffer
size_t data_size; // input buffer size 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) size_t offset; // offset to main data chunk (VP8 or VP8L)
const uint8_t* alpha_data; // points to alpha chunk (if present) const uint8_t* alpha_data; // points to alpha chunk (if present)
size_t alpha_data_size; // alpha chunk size size_t alpha_data_size; // alpha chunk size