mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +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)) { \
|
||||
err = ChunkAssignData(&chunk, data, copy_data, tag); \
|
||||
if (err == WEBP_MUX_OK) { \
|
||||
err = ChunkSetNth(&chunk, (LIST), nth); \
|
||||
err = ChunkSetHead(&chunk, (LIST)); \
|
||||
} \
|
||||
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) {
|
||||
WebPChunk chunk;
|
||||
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;
|
||||
|
||||
// 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'.
|
||||
@ -202,7 +202,7 @@ static WebPMuxError AddDataToChunkList(
|
||||
ChunkInit(&chunk);
|
||||
err = ChunkAssignData(&chunk, data, copy_data, tag);
|
||||
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;
|
||||
return WEBP_MUX_OK;
|
||||
Err:
|
||||
@ -342,7 +342,7 @@ WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
|
||||
// Set the animation parameters.
|
||||
PutLE32(data, params->bgcolor);
|
||||
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,
|
||||
@ -540,7 +540,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||
PutLE24(data + 4, width - 1); // canvas width.
|
||||
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.
|
||||
|
@ -127,11 +127,14 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
|
||||
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||
int copy_data, uint32_t tag);
|
||||
|
||||
// Sets 'chunk' at nth position in the 'chunk_list'.
|
||||
// nth = 0 has the special meaning "last of the list".
|
||||
// Sets 'chunk' as the only element in 'chunk_list' if it is empty.
|
||||
// On success ownership is transferred from 'chunk' to the 'chunk_list'.
|
||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
uint32_t nth);
|
||||
WebPMuxError ChunkSetHead(WebPChunk* chunk, WebPChunk** chunk_list);
|
||||
// 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_.
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@ -156,11 +135,12 @@ WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
uint32_t nth) {
|
||||
WebPMuxError ChunkSetHead(WebPChunk* const chunk,
|
||||
WebPChunk** const chunk_list) {
|
||||
WebPChunk* new_chunk;
|
||||
|
||||
if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) {
|
||||
assert(chunk_list != NULL);
|
||||
if (*chunk_list != NULL) {
|
||||
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;
|
||||
*new_chunk = *chunk;
|
||||
chunk->owner_ = 0;
|
||||
new_chunk->next_ = *chunk_list;
|
||||
new_chunk->next_ = NULL;
|
||||
*chunk_list = new_chunk;
|
||||
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).
|
||||
|
||||
|
@ -103,6 +103,7 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
||||
const uint8_t* const last = bytes + size;
|
||||
WebPChunk subchunk;
|
||||
size_t subchunk_size;
|
||||
WebPChunk** unknown_chunk_list = &wpi->unknown_;
|
||||
ChunkInit(&subchunk);
|
||||
|
||||
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;
|
||||
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.
|
||||
|
||||
// Rest of the chunks.
|
||||
@ -134,19 +135,23 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
||||
switch (ChunkGetIdFromTag(subchunk.tag_)) {
|
||||
case WEBP_CHUNK_ALPHA:
|
||||
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.
|
||||
break;
|
||||
case WEBP_CHUNK_IMAGE:
|
||||
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;
|
||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||
break;
|
||||
case WEBP_CHUNK_UNKNOWN:
|
||||
if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
|
||||
// before some image chunks.
|
||||
if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
|
||||
if (wpi->is_partial_) {
|
||||
goto Fail; // Encountered an unknown chunk
|
||||
// before some image chunks.
|
||||
}
|
||||
if (ChunkAppend(&subchunk, &unknown_chunk_list) != WEBP_MUX_OK) {
|
||||
goto Fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto Fail;
|
||||
@ -177,6 +182,9 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
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);
|
||||
|
||||
// Sanity checks.
|
||||
@ -230,7 +238,6 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
while (data != end) {
|
||||
size_t data_size;
|
||||
WebPChunkId id;
|
||||
WebPChunk** chunk_list;
|
||||
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
|
||||
copy_data) != WEBP_MUX_OK) {
|
||||
goto Err;
|
||||
@ -240,11 +247,11 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
switch (id) {
|
||||
case WEBP_CHUNK_ALPHA:
|
||||
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.
|
||||
break;
|
||||
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;
|
||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||
PushImage:
|
||||
@ -261,8 +268,11 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
default: // A non-image chunk.
|
||||
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
|
||||
// getting all chunks of an image.
|
||||
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
|
||||
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
|
||||
if (chunk_list_ends[id] == NULL) {
|
||||
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 (data_size < CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE) goto Err;
|
||||
mux->canvas_width_ = GetLE24(data + 12) + 1;
|
||||
|
Loading…
Reference in New Issue
Block a user