mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-28 14:38:21 +01:00
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:
parent
393f89b763
commit
1684f4ee37
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user