mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
Speed-up chunk list operations.
The chunk list only has two operations: append and set to one element. The two operations are split and the append one is sped up by storing the last element. Corrupted data could make a very long list to search through. BUG=oss-fuzz:9190 Change-Id: I1aa813ca629df29efaa3b46dbd4c4c42dbeaa34c
This commit is contained in:
parent
2281bbf6f7
commit
a9ceda7ff1
@ -69,12 +69,12 @@ void WebPMuxDelete(WebPMux* mux) {
|
|||||||
if (idx == (INDEX)) { \
|
if (idx == (INDEX)) { \
|
||||||
err = ChunkAssignData(&chunk, data, copy_data, tag); \
|
err = ChunkAssignData(&chunk, data, copy_data, tag); \
|
||||||
if (err == WEBP_MUX_OK) { \
|
if (err == WEBP_MUX_OK) { \
|
||||||
err = ChunkSetNth(&chunk, (LIST), nth); \
|
err = ChunkSetHead(&chunk, (LIST)); \
|
||||||
} \
|
} \
|
||||||
return err; \
|
return err; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth,
|
static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag,
|
||||||
const WebPData* const data, int copy_data) {
|
const WebPData* const data, int copy_data) {
|
||||||
WebPChunk chunk;
|
WebPChunk chunk;
|
||||||
WebPMuxError err = WEBP_MUX_NOT_FOUND;
|
WebPMuxError err = WEBP_MUX_NOT_FOUND;
|
||||||
@ -190,7 +190,7 @@ WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4],
|
|||||||
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
||||||
|
|
||||||
// Add the given chunk.
|
// Add the given chunk.
|
||||||
return MuxSet(mux, tag, 1, chunk_data, copy_data);
|
return MuxSet(mux, tag, chunk_data, copy_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'.
|
// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'.
|
||||||
@ -202,7 +202,7 @@ static WebPMuxError AddDataToChunkList(
|
|||||||
ChunkInit(&chunk);
|
ChunkInit(&chunk);
|
||||||
err = ChunkAssignData(&chunk, data, copy_data, tag);
|
err = ChunkAssignData(&chunk, data, copy_data, tag);
|
||||||
if (err != WEBP_MUX_OK) goto Err;
|
if (err != WEBP_MUX_OK) goto Err;
|
||||||
err = ChunkSetNth(&chunk, chunk_list, 1);
|
err = ChunkSetHead(&chunk, chunk_list);
|
||||||
if (err != WEBP_MUX_OK) goto Err;
|
if (err != WEBP_MUX_OK) goto Err;
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
Err:
|
Err:
|
||||||
@ -342,7 +342,7 @@ WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
|
|||||||
// Set the animation parameters.
|
// Set the animation parameters.
|
||||||
PutLE32(data, params->bgcolor);
|
PutLE32(data, params->bgcolor);
|
||||||
PutLE16(data + 4, params->loop_count);
|
PutLE16(data + 4, params->loop_count);
|
||||||
return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1);
|
return MuxSet(mux, kChunks[IDX_ANIM].tag, &anim, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
|
WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
|
||||||
@ -540,7 +540,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
|||||||
PutLE24(data + 4, width - 1); // canvas width.
|
PutLE24(data + 4, width - 1); // canvas width.
|
||||||
PutLE24(data + 7, height - 1); // canvas height.
|
PutLE24(data + 7, height - 1); // canvas height.
|
||||||
|
|
||||||
return MuxSet(mux, kChunks[IDX_VP8X].tag, 1, &vp8x, 1);
|
return MuxSet(mux, kChunks[IDX_VP8X].tag, &vp8x, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleans up 'mux' by removing any unnecessary chunks.
|
// Cleans up 'mux' by removing any unnecessary chunks.
|
||||||
|
@ -127,11 +127,14 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
|
|||||||
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||||
int copy_data, uint32_t tag);
|
int copy_data, uint32_t tag);
|
||||||
|
|
||||||
// Sets 'chunk' at nth position in the 'chunk_list'.
|
// Sets 'chunk' as the only element in 'chunk_list' if it is empty.
|
||||||
// nth = 0 has the special meaning "last of the list".
|
|
||||||
// On success ownership is transferred from 'chunk' to the 'chunk_list'.
|
// On success ownership is transferred from 'chunk' to the 'chunk_list'.
|
||||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
WebPMuxError ChunkSetHead(WebPChunk* chunk, WebPChunk** chunk_list);
|
||||||
uint32_t nth);
|
// Sets 'chunk' at last position in the 'chunk_list'.
|
||||||
|
// On success ownership is transferred from 'chunk' to the 'chunk_list'.
|
||||||
|
// *chunk_list also points towards the last valid element of the initial
|
||||||
|
// *chunk_list.
|
||||||
|
WebPMuxError ChunkAppend(WebPChunk* chunk, WebPChunk*** chunk_list);
|
||||||
|
|
||||||
// Releases chunk and returns chunk->next_.
|
// Releases chunk and returns chunk->next_.
|
||||||
WebPChunk* ChunkRelease(WebPChunk* const chunk);
|
WebPChunk* ChunkRelease(WebPChunk* const chunk);
|
||||||
|
@ -111,27 +111,6 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
|
|||||||
return ((nth > 0) && (iter > 0)) ? NULL : first;
|
return ((nth > 0) && (iter > 0)) ? NULL : first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Outputs a pointer to 'prev_chunk->next_',
|
|
||||||
// where 'prev_chunk' is the pointer to the chunk at position (nth - 1).
|
|
||||||
// Returns true if nth chunk was found.
|
|
||||||
static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
|
|
||||||
WebPChunk*** const location) {
|
|
||||||
uint32_t count = 0;
|
|
||||||
assert(chunk_list != NULL);
|
|
||||||
*location = chunk_list;
|
|
||||||
|
|
||||||
while (*chunk_list != NULL) {
|
|
||||||
WebPChunk* const cur_chunk = *chunk_list;
|
|
||||||
++count;
|
|
||||||
if (count == nth) return 1; // Found.
|
|
||||||
chunk_list = &cur_chunk->next_;
|
|
||||||
*location = chunk_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// *chunk_list is ok to be NULL if adding at last location.
|
|
||||||
return (nth == 0 || (count == nth - 1)) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Chunk writer methods.
|
// Chunk writer methods.
|
||||||
|
|
||||||
@ -156,11 +135,12 @@ WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
|||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
WebPMuxError ChunkSetHead(WebPChunk* const chunk,
|
||||||
uint32_t nth) {
|
WebPChunk** const chunk_list) {
|
||||||
WebPChunk* new_chunk;
|
WebPChunk* new_chunk;
|
||||||
|
|
||||||
if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) {
|
assert(chunk_list != NULL);
|
||||||
|
if (*chunk_list != NULL) {
|
||||||
return WEBP_MUX_NOT_FOUND;
|
return WEBP_MUX_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,11 +148,26 @@ WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
|||||||
if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
|
if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||||
*new_chunk = *chunk;
|
*new_chunk = *chunk;
|
||||||
chunk->owner_ = 0;
|
chunk->owner_ = 0;
|
||||||
new_chunk->next_ = *chunk_list;
|
new_chunk->next_ = NULL;
|
||||||
*chunk_list = new_chunk;
|
*chunk_list = new_chunk;
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebPMuxError ChunkAppend(WebPChunk* const chunk,
|
||||||
|
WebPChunk*** const chunk_list) {
|
||||||
|
assert(chunk_list != NULL && *chunk_list != NULL);
|
||||||
|
|
||||||
|
if (**chunk_list == NULL) {
|
||||||
|
ChunkSetHead(chunk, *chunk_list);
|
||||||
|
} else {
|
||||||
|
WebPChunk* last_chunk = **chunk_list;
|
||||||
|
while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_;
|
||||||
|
ChunkSetHead(chunk, &last_chunk->next_);
|
||||||
|
*chunk_list = &last_chunk->next_;
|
||||||
|
}
|
||||||
|
return WEBP_MUX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Chunk deletion method(s).
|
// Chunk deletion method(s).
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
|||||||
const uint8_t* const last = bytes + size;
|
const uint8_t* const last = bytes + size;
|
||||||
WebPChunk subchunk;
|
WebPChunk subchunk;
|
||||||
size_t subchunk_size;
|
size_t subchunk_size;
|
||||||
|
WebPChunk** unknown_chunk_list = &wpi->unknown_;
|
||||||
ChunkInit(&subchunk);
|
ChunkInit(&subchunk);
|
||||||
|
|
||||||
assert(chunk->tag_ == kChunks[IDX_ANMF].tag);
|
assert(chunk->tag_ == kChunks[IDX_ANMF].tag);
|
||||||
@ -117,7 +118,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
|||||||
if (size < hdr_size) goto Fail;
|
if (size < hdr_size) goto Fail;
|
||||||
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
|
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
|
||||||
}
|
}
|
||||||
ChunkSetNth(&subchunk, &wpi->header_, 1);
|
ChunkSetHead(&subchunk, &wpi->header_);
|
||||||
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
|
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
|
||||||
|
|
||||||
// Rest of the chunks.
|
// Rest of the chunks.
|
||||||
@ -134,19 +135,23 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
|||||||
switch (ChunkGetIdFromTag(subchunk.tag_)) {
|
switch (ChunkGetIdFromTag(subchunk.tag_)) {
|
||||||
case WEBP_CHUNK_ALPHA:
|
case WEBP_CHUNK_ALPHA:
|
||||||
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
|
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
|
||||||
if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
|
if (ChunkSetHead(&subchunk, &wpi->alpha_) != WEBP_MUX_OK) goto Fail;
|
||||||
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_IMAGE:
|
case WEBP_CHUNK_IMAGE:
|
||||||
if (wpi->img_ != NULL) goto Fail; // Only 1 image chunk allowed.
|
if (wpi->img_ != NULL) goto Fail; // Only 1 image chunk allowed.
|
||||||
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
if (ChunkSetHead(&subchunk, &wpi->img_) != WEBP_MUX_OK) goto Fail;
|
||||||
if (!MuxImageFinalize(wpi)) goto Fail;
|
if (!MuxImageFinalize(wpi)) goto Fail;
|
||||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_UNKNOWN:
|
case WEBP_CHUNK_UNKNOWN:
|
||||||
if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
|
if (wpi->is_partial_) {
|
||||||
|
goto Fail; // Encountered an unknown chunk
|
||||||
// before some image chunks.
|
// before some image chunks.
|
||||||
if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
|
}
|
||||||
|
if (ChunkAppend(&subchunk, &unknown_chunk_list) != WEBP_MUX_OK) {
|
||||||
|
goto Fail;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
goto Fail;
|
goto Fail;
|
||||||
@ -177,6 +182,9 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
WebPChunk chunk;
|
WebPChunk chunk;
|
||||||
|
// Stores the end of the chunk lists so that it is faster to append data to
|
||||||
|
// their ends.
|
||||||
|
WebPChunk** chunk_list_ends[WEBP_CHUNK_NIL + 1] = { NULL };
|
||||||
ChunkInit(&chunk);
|
ChunkInit(&chunk);
|
||||||
|
|
||||||
// Sanity checks.
|
// Sanity checks.
|
||||||
@ -230,7 +238,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
while (data != end) {
|
while (data != end) {
|
||||||
size_t data_size;
|
size_t data_size;
|
||||||
WebPChunkId id;
|
WebPChunkId id;
|
||||||
WebPChunk** chunk_list;
|
|
||||||
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
|
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
|
||||||
copy_data) != WEBP_MUX_OK) {
|
copy_data) != WEBP_MUX_OK) {
|
||||||
goto Err;
|
goto Err;
|
||||||
@ -240,11 +247,11 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
switch (id) {
|
switch (id) {
|
||||||
case WEBP_CHUNK_ALPHA:
|
case WEBP_CHUNK_ALPHA:
|
||||||
if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
|
if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
|
||||||
if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
|
if (ChunkSetHead(&chunk, &wpi->alpha_) != WEBP_MUX_OK) goto Err;
|
||||||
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_IMAGE:
|
case WEBP_CHUNK_IMAGE:
|
||||||
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
|
if (ChunkSetHead(&chunk, &wpi->img_) != WEBP_MUX_OK) goto Err;
|
||||||
if (!MuxImageFinalize(wpi)) goto Err;
|
if (!MuxImageFinalize(wpi)) goto Err;
|
||||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||||
PushImage:
|
PushImage:
|
||||||
@ -261,8 +268,11 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
default: // A non-image chunk.
|
default: // A non-image chunk.
|
||||||
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
|
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
|
||||||
// getting all chunks of an image.
|
// getting all chunks of an image.
|
||||||
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
|
if (chunk_list_ends[id] == NULL) {
|
||||||
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
|
chunk_list_ends[id] =
|
||||||
|
MuxGetChunkListFromId(mux, id); // List to add this chunk.
|
||||||
|
}
|
||||||
|
if (ChunkAppend(&chunk, &chunk_list_ends[id]) != WEBP_MUX_OK) goto Err;
|
||||||
if (id == WEBP_CHUNK_VP8X) { // grab global specs
|
if (id == WEBP_CHUNK_VP8X) { // grab global specs
|
||||||
if (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err;
|
if (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err;
|
||||||
mux->canvas_width_ = GetLE24(data + 12) + 1;
|
mux->canvas_width_ = GetLE24(data + 12) + 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user