mirror of https://github.com/google/oss-fuzz.git
[libwebp] more targets (#1552)
* new target: advanced decoding api * new target: animation decoding api * new target: (de)mux API * prefer copying dict to using options files
This commit is contained in:
parent
9d49131473
commit
8eeffa627f
|
@ -20,5 +20,8 @@ RUN apt-get update && apt-get install -y autoconf make libtool
|
|||
RUN git clone https://chromium.googlesource.com/webm/libwebp
|
||||
ADD http://cdn.pwmon.org/oss-fuzz/libwebp/fuzz_seed_corpus.zip $SRC/
|
||||
COPY build.sh fuzz.h fuzz.dict $SRC/
|
||||
COPY fuzz_simple_api.c fuzz_simple_api.options $SRC/
|
||||
COPY fuzz_simple_api.c $SRC/
|
||||
COPY fuzz_advanced_api.c $SRC/
|
||||
COPY fuzz_animation_api.c $SRC/
|
||||
COPY fuzz_demux_api.c $SRC/
|
||||
WORKDIR libwebp
|
||||
|
|
|
@ -21,6 +21,7 @@ WEBP_CFLAGS="$CFLAGS -DWEBP_MAX_IMAGE_SIZE=838860800" # 800MiB
|
|||
./autogen.sh
|
||||
CFLAGS="$WEBP_CFLAGS" ./configure \
|
||||
--enable-libwebpdemux \
|
||||
--enable-libwebpmux \
|
||||
--disable-shared \
|
||||
--disable-jpeg \
|
||||
--disable-tiff \
|
||||
|
@ -37,4 +38,30 @@ $CXX $CXXFLAGS -lFuzzingEngine \
|
|||
fuzz_simple_api.o -o $OUT/fuzz_simple_api \
|
||||
src/.libs/libwebp.a
|
||||
cp $SRC/fuzz_seed_corpus.zip $OUT/fuzz_simple_api_seed_corpus.zip
|
||||
cp $SRC/fuzz_simple_api.options $OUT
|
||||
cp $SRC/fuzz.dict $OUT/fuzz_simple_api.dict
|
||||
|
||||
# Advanced Decoding API
|
||||
$CC $CFLAGS -Isrc -c $SRC/fuzz_advanced_api.c
|
||||
$CXX $CXXFLAGS -lFuzzingEngine \
|
||||
fuzz_advanced_api.o -o $OUT/fuzz_advanced_api \
|
||||
src/.libs/libwebp.a
|
||||
cp $SRC/fuzz_seed_corpus.zip $OUT/fuzz_advanced_api_seed_corpus.zip
|
||||
cp $SRC/fuzz.dict $OUT/fuzz_advanced_api.dict
|
||||
|
||||
# Animation Decoding API
|
||||
$CC $CFLAGS -Isrc -c $SRC/fuzz_animation_api.c
|
||||
$CXX $CXXFLAGS -lFuzzingEngine \
|
||||
fuzz_animation_api.o -o $OUT/fuzz_animation_api \
|
||||
src/demux/.libs/libwebpdemux.a \
|
||||
src/.libs/libwebp.a
|
||||
cp $SRC/fuzz_seed_corpus.zip $OUT/fuzz_animation_api_seed_corpus.zip
|
||||
cp $SRC/fuzz.dict $OUT/fuzz_animation_api.dict
|
||||
|
||||
# (De)mux API
|
||||
$CC $CFLAGS -Isrc -c $SRC/fuzz_demux_api.c
|
||||
$CXX $CXXFLAGS -lFuzzingEngine \
|
||||
fuzz_demux_api.o -o $OUT/fuzz_demux_api \
|
||||
src/demux/.libs/libwebpdemux.a src/mux/.libs/libwebpmux.a \
|
||||
src/.libs/libwebp.a
|
||||
cp $SRC/fuzz_seed_corpus.zip $OUT/fuzz_demux_api_seed_corpus.zip
|
||||
cp $SRC/fuzz.dict $OUT/fuzz_demux_api.dict
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Arbitrary limit to prevent OOM, timeout, or slow execution.
|
||||
// Arbitrary limits to prevent OOM, timeout, or slow execution.
|
||||
//
|
||||
// The decoded image size, and for animations additionally the canvas size.
|
||||
static const size_t fuzz_px_limit = 1024 * 1024;
|
||||
// Demuxed or decoded animation frames.
|
||||
static const int fuzz_frame_limit = 3;
|
||||
|
||||
// Reads and sums (up to) 128 spread-out bytes.
|
||||
static uint8_t fuzz_hash(const uint8_t* data, size_t size) {
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
#include "fuzz.h"
|
||||
#include "webp/decode.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
WebPDecoderConfig config;
|
||||
if (!WebPInitDecoderConfig(&config))
|
||||
return 0;
|
||||
if (WebPGetFeatures(data, size, &config.input) != VP8_STATUS_OK)
|
||||
return 0;
|
||||
if ((size_t)config.input.width * config.input.height > fuzz_px_limit)
|
||||
return 0;
|
||||
|
||||
// Using two independent criteria ensures that all combinations of options
|
||||
// can reach each path at the decoding stage, with meaningful differences.
|
||||
|
||||
const uint8_t value = fuzz_hash(data, size);
|
||||
float factor = value / 255.f; // 0-1
|
||||
|
||||
config.options.flip = value & 1;
|
||||
config.options.bypass_filtering = value & 2;
|
||||
config.options.no_fancy_upsampling = value & 4;
|
||||
config.options.use_threads = value & 8;
|
||||
if (size & 1) {
|
||||
config.options.use_cropping = 1;
|
||||
config.options.crop_width = (int)(config.input.width * (1 - factor));
|
||||
config.options.crop_height = (int)(config.input.height * (1 - factor));
|
||||
config.options.crop_left = config.input.width - config.options.crop_width;
|
||||
config.options.crop_top = config.input.height - config.options.crop_height;
|
||||
}
|
||||
if (size & 2) {
|
||||
int strength = (int)(factor * 100);
|
||||
config.options.dithering_strength = strength;
|
||||
config.options.alpha_dithering_strength = 100 - strength;
|
||||
}
|
||||
if (size & 4) {
|
||||
config.options.use_scaling = 1;
|
||||
config.options.scaled_width = (int)(config.input.width * factor * 2);
|
||||
config.options.scaled_height = (int)(config.input.height * factor * 2);
|
||||
}
|
||||
|
||||
config.output.colorspace = (WEBP_CSP_MODE)(value % MODE_LAST);
|
||||
|
||||
if (size % 3) {
|
||||
// Decodes incrementally in chunks of increasing size.
|
||||
WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
|
||||
if (!idec)
|
||||
return 0;
|
||||
VP8StatusCode status;
|
||||
if (size & 8) {
|
||||
size_t available_size = value + 1;
|
||||
while (1) {
|
||||
if (available_size > size)
|
||||
available_size = size;
|
||||
status = WebPIUpdate(idec, data, available_size);
|
||||
if (status != VP8_STATUS_SUSPENDED || available_size == size)
|
||||
break;
|
||||
available_size *= 2;
|
||||
}
|
||||
} else {
|
||||
// WebPIAppend expects new data and its size with each call.
|
||||
// Implemented here by simply advancing the pointer into data.
|
||||
const uint8_t* new_data = data;
|
||||
size_t new_size = value + 1;
|
||||
while (1) {
|
||||
if (new_data + new_size > data + size)
|
||||
new_size = data + size - new_data;
|
||||
status = WebPIAppend(idec, new_data, new_size);
|
||||
if (status != VP8_STATUS_SUSPENDED || new_size == 0)
|
||||
break;
|
||||
new_data += new_size;
|
||||
new_size *= 2;
|
||||
}
|
||||
}
|
||||
WebPIDelete(idec);
|
||||
} else {
|
||||
WebPDecode(data, size, &config);
|
||||
}
|
||||
|
||||
WebPFreeDecBuffer(&config.output);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#include "fuzz.h"
|
||||
#include "webp/decode.h"
|
||||
#include "webp/demux.h"
|
||||
#include "webp/mux_types.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
WebPData webp_data;
|
||||
WebPDataInit(&webp_data);
|
||||
webp_data.size = size;
|
||||
webp_data.bytes = data;
|
||||
|
||||
// WebPAnimDecoderNew uses WebPDemux internally to calloc canvas size.
|
||||
WebPDemuxer* demux = WebPDemux(&webp_data);
|
||||
if (!demux)
|
||||
return 0;
|
||||
uint32_t cw = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
|
||||
uint32_t ch = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
|
||||
if ((size_t)cw * ch > fuzz_px_limit) {
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In addition to canvas size, check each frame separately.
|
||||
WebPIterator iter;
|
||||
for (int i = 0; i < fuzz_frame_limit; i++) {
|
||||
if (!WebPDemuxGetFrame(demux, i + 1, &iter))
|
||||
break;
|
||||
int w, h;
|
||||
if (WebPGetInfo(iter.fragment.bytes, iter.fragment.size, &w, &h)) {
|
||||
if ((size_t)w * h > fuzz_px_limit) { // image size of the frame payload
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demux);
|
||||
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
if (!WebPAnimDecoderOptionsInit(&dec_options))
|
||||
return 0;
|
||||
|
||||
dec_options.use_threads = size & 1;
|
||||
// Animations only support 4 (of 12) modes.
|
||||
dec_options.color_mode = (WEBP_CSP_MODE)(size % MODE_LAST);
|
||||
if (dec_options.color_mode != MODE_BGRA &&
|
||||
dec_options.color_mode != MODE_rgbA &&
|
||||
dec_options.color_mode != MODE_bgrA) {
|
||||
dec_options.color_mode = MODE_RGBA;
|
||||
}
|
||||
|
||||
WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data, &dec_options);
|
||||
if (!dec)
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < fuzz_frame_limit; i++) {
|
||||
uint8_t* buf;
|
||||
int timestamp;
|
||||
if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp))
|
||||
break;
|
||||
}
|
||||
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#include "fuzz.h"
|
||||
#include "webp/mux.h"
|
||||
#include "webp/demux.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
WebPData webp_data;
|
||||
WebPDataInit(&webp_data);
|
||||
webp_data.size = size;
|
||||
webp_data.bytes = data;
|
||||
|
||||
// Extracted chunks and frames are not processed or decoded,
|
||||
// which is already covered extensively by the other fuzz targets.
|
||||
|
||||
if (size & 1) {
|
||||
// Mux API
|
||||
WebPMux* mux = WebPMuxCreate(&webp_data, size & 2);
|
||||
if (!mux)
|
||||
return 0;
|
||||
|
||||
WebPData chunk;
|
||||
WebPMuxGetChunk(mux, "EXIF", &chunk);
|
||||
WebPMuxGetChunk(mux, "ICCP", &chunk);
|
||||
WebPMuxGetChunk(mux, "FUZZ", &chunk); // unknown
|
||||
|
||||
uint32_t flags;
|
||||
WebPMuxGetFeatures(mux, &flags);
|
||||
|
||||
WebPMuxAnimParams params;
|
||||
WebPMuxGetAnimationParams(mux, ¶ms);
|
||||
|
||||
WebPMuxError status;
|
||||
WebPMuxFrameInfo info;
|
||||
for (int i = 0; i < fuzz_frame_limit; i++) {
|
||||
status = WebPMuxGetFrame(mux, i + 1, &info);
|
||||
if (status == WEBP_MUX_NOT_FOUND) {
|
||||
break;
|
||||
} else if (status == WEBP_MUX_OK) {
|
||||
WebPDataClear(&info.bitstream);
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxDelete(mux);
|
||||
} else {
|
||||
// Demux API
|
||||
WebPDemuxer* demux;
|
||||
if (size & 2) {
|
||||
WebPDemuxState state;
|
||||
demux = WebPDemuxPartial(&webp_data, &state);
|
||||
if (state < WEBP_DEMUX_PARSED_HEADER) {
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
demux = WebPDemux(&webp_data);
|
||||
if (!demux)
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebPChunkIterator chunk_iter;
|
||||
if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter))
|
||||
WebPDemuxNextChunk(&chunk_iter);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
if (WebPDemuxGetChunk(demux, "ICCP", 0, &chunk_iter)) // 0 == last
|
||||
WebPDemuxPrevChunk(&chunk_iter);
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
// Skips FUZZ because the Demux API has no concept of (un)known chunks.
|
||||
|
||||
WebPIterator iter;
|
||||
if (WebPDemuxGetFrame(demux, 1, &iter)) {
|
||||
for (int i = 1; i < fuzz_frame_limit; i++) {
|
||||
if (!WebPDemuxNextFrame(&iter))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demux);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -11,9 +11,8 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|||
const uint8_t value = fuzz_hash(data, size);
|
||||
uint8_t* buf = NULL;
|
||||
|
||||
// This is verbose, but covers all available variants.
|
||||
// For functions that decode into an external buffer, an intentionally
|
||||
// too small buffer can be given with low probability.
|
||||
// For *Into functions, which decode into an external buffer, an
|
||||
// intentionally too small buffer can be given with low probability.
|
||||
if (value < 0x16) {
|
||||
buf = WebPDecodeRGBA(data, size, &w, &h);
|
||||
} else if (value < 0x2b) {
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[libfuzzer]
|
||||
dict = fuzz.dict
|
Loading…
Reference in New Issue