mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-25 05:08:21 +01:00
Switch public fuzz tests to fuzztest.
Change-Id: I75afb65058690585bbf2671c27d6a99a87bcaab7
This commit is contained in:
parent
64186bb36c
commit
4f200de591
@ -45,6 +45,7 @@ option(WEBP_BUILD_LIBWEBPMUX "Build the libwebpmux library." ON)
|
||||
option(WEBP_BUILD_WEBPMUX "Build the webpmux command line tool." ON)
|
||||
option(WEBP_BUILD_EXTRAS "Build extras." ON)
|
||||
option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF)
|
||||
option(WEBP_BUILD_FUZZTEST "Build the fuzztest tests." OFF)
|
||||
option(WEBP_USE_THREAD "Enable threading support" ON)
|
||||
option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
|
||||
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
|
||||
@ -375,9 +376,11 @@ if(XCODE)
|
||||
endif()
|
||||
target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES})
|
||||
target_include_directories(
|
||||
webpdecoder PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
webpdecoder
|
||||
PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>"
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
set_target_properties(
|
||||
webpdecoder
|
||||
PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
|
||||
@ -771,6 +774,10 @@ if(WEBP_BUILD_ANIM_UTILS)
|
||||
target_include_directories(anim_dump PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
|
||||
endif()
|
||||
|
||||
if(WEBP_BUILD_FUZZTEST)
|
||||
add_subdirectory(tests/fuzzer)
|
||||
endif()
|
||||
|
||||
# Install the different headers and libraries.
|
||||
install(
|
||||
TARGETS ${INSTALLED_LIBRARIES}
|
||||
|
@ -11,8 +11,9 @@ https://chromium.googlesource.com/webm/libwebp-test-data
|
||||
Follow the [build instructions](../doc/building.md) for libwebp, optionally
|
||||
adding build flags for various sanitizers (e.g., -fsanitize=address).
|
||||
|
||||
`fuzzer/makefile.unix` can then be used to compile the fuzzer targets:
|
||||
`-DWEBP_BUILD_FUZZTEST=ON` can then be used to compile the fuzzer targets:
|
||||
|
||||
```shell
|
||||
$ make -C fuzzer -f makefile.unix
|
||||
$ cmake -B ./build -S . -DWEBP_BUILD_FUZZTEST=ON
|
||||
$ make -C build
|
||||
```
|
||||
|
61
tests/fuzzer/CMakeLists.txt
Normal file
61
tests/fuzzer/CMakeLists.txt
Normal file
@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2024 Google LLC
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license
|
||||
# that can be found in the LICENSE file in the root of the source
|
||||
# tree. An additional intellectual property rights grant can be found
|
||||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
# Adds a fuzztest from file TEST_NAME.cc located in the gtest folder. Extra
|
||||
# arguments are considered as extra source files.
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.19.0")
|
||||
return()
|
||||
endif()
|
||||
|
||||
macro(add_webp_fuzztest TEST_NAME)
|
||||
add_executable(${TEST_NAME} ${TEST_NAME}.cc)
|
||||
# FuzzTest bundles GoogleTest so no need to link to gtest libraries.
|
||||
target_link_libraries(${TEST_NAME} PRIVATE fuzz_utils webp ${ARGN})
|
||||
link_fuzztest(${TEST_NAME})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
set_property(
|
||||
TEST ${TEST_NAME}
|
||||
PROPERTY ENVIRONMENT "TEST_DATA_DIRS=${CMAKE_CURRENT_SOURCE_DIR}/data/")
|
||||
endmacro()
|
||||
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(FETCHCONTENT_QUIET FALSE)
|
||||
set(fuzztest_SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/fuzztest-src)
|
||||
FetchContent_Declare(
|
||||
fuzztest
|
||||
GIT_REPOSITORY https://github.com/google/fuzztest.git
|
||||
GIT_TAG a40caf40aaf621dd0e04f9d8b47d1153fd2682d2
|
||||
GIT_PROGRESS TRUE
|
||||
PATCH_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/patch.sh)
|
||||
|
||||
FetchContent_MakeAvailable(fuzztest)
|
||||
|
||||
fuzztest_setup_fuzzing_flags()
|
||||
|
||||
add_library(fuzz_utils fuzz_utils.h fuzz_utils.cc img_alpha.h img_grid.h
|
||||
img_peak.h)
|
||||
target_link_libraries(fuzz_utils PUBLIC webpdecoder)
|
||||
link_fuzztest(fuzz_utils)
|
||||
|
||||
add_webp_fuzztest(advanced_api_fuzzer)
|
||||
add_webp_fuzztest(enc_dec_fuzzer)
|
||||
add_webp_fuzztest(huffman_fuzzer)
|
||||
add_webp_fuzztest(simple_api_fuzzer)
|
||||
|
||||
if(WEBP_BUILD_LIBWEBPMUX)
|
||||
add_webp_fuzztest(animation_api_fuzzer webpdemux)
|
||||
add_webp_fuzztest(animdecoder_fuzzer imageioutil webpdemux)
|
||||
add_webp_fuzztest(animencoder_fuzzer libwebpmux)
|
||||
add_webp_fuzztest(mux_demux_api_fuzzer libwebpmux webpdemux)
|
||||
endif()
|
@ -14,54 +14,59 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/utils/rescaler_utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
namespace {
|
||||
|
||||
void AdvancedApiTest(std::string_view blob, uint8_t factor_u8, bool flip,
|
||||
bool bypass_filtering, bool no_fancy_upsampling,
|
||||
bool use_threads, bool use_cropping, bool use_scaling,
|
||||
bool use_dithering, int colorspace, bool incremental) {
|
||||
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 > kFuzzPxLimit) return 0;
|
||||
if (!WebPInitDecoderConfig(&config)) return;
|
||||
const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
const size_t size = blob.size();
|
||||
if (WebPGetFeatures(data, size, &config.input) != VP8_STATUS_OK) return;
|
||||
if ((size_t)config.input.width * config.input.height >
|
||||
fuzz_utils::kFuzzPxLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 = FuzzHash(data, size);
|
||||
const float factor = value / 255.f; // 0-1
|
||||
const uint8_t value = fuzz_utils::FuzzHash(data, size);
|
||||
const float factor = factor_u8 / 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.flip = flip;
|
||||
config.options.bypass_filtering = bypass_filtering;
|
||||
config.options.no_fancy_upsampling = no_fancy_upsampling;
|
||||
config.options.use_threads = use_threads;
|
||||
if (use_cropping) {
|
||||
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) {
|
||||
if (use_dithering) {
|
||||
int strength = (int)(factor * 100);
|
||||
config.options.dithering_strength = strength;
|
||||
config.options.alpha_dithering_strength = 100 - strength;
|
||||
}
|
||||
if (size & 4) {
|
||||
if (use_scaling) {
|
||||
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);
|
||||
}
|
||||
|
||||
#if defined(WEBP_REDUCE_CSP)
|
||||
config.output.colorspace = (value & 1)
|
||||
? ((value & 2) ? MODE_RGBA : MODE_BGRA)
|
||||
: ((value & 2) ? MODE_rgbA : MODE_bgrA);
|
||||
#else
|
||||
config.output.colorspace = (WEBP_CSP_MODE)(value % MODE_LAST);
|
||||
#endif // WEBP_REDUCE_CSP
|
||||
config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i == 1) {
|
||||
@ -80,7 +85,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
if (WebPRescalerGetScaledDimensions(config.input.width,
|
||||
config.input.height, &scaled_width,
|
||||
&scaled_height)) {
|
||||
size_t fuzz_px_limit = kFuzzPxLimit;
|
||||
size_t fuzz_px_limit = fuzz_utils::kFuzzPxLimit;
|
||||
if (scaled_width != config.input.width ||
|
||||
scaled_height != config.input.height) {
|
||||
// Using the WebPRescalerImport internally can significantly slow
|
||||
@ -92,18 +97,18 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
// very wide input image to a very tall canvas can be as slow as
|
||||
// decoding a huge number of pixels. Avoid timeouts due to these.
|
||||
const uint64_t max_num_operations =
|
||||
(uint64_t)Max(scaled_width, config.input.width) *
|
||||
Max(scaled_height, config.input.height);
|
||||
(uint64_t)std::max(scaled_width, config.input.width) *
|
||||
std::max(scaled_height, config.input.height);
|
||||
if (max_num_operations > fuzz_px_limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (size % 3) {
|
||||
if (incremental) {
|
||||
// Decodes incrementally in chunks of increasing size.
|
||||
WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
|
||||
if (!idec) return 0;
|
||||
if (!idec) return;
|
||||
VP8StatusCode status;
|
||||
if (size & 8) {
|
||||
size_t available_size = value + 1;
|
||||
@ -135,5 +140,28 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
|
||||
WebPFreeDecBuffer(&config.output);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(AdvancedApi, AdvancedApiTest)
|
||||
.WithDomains(
|
||||
fuzztest::String()
|
||||
.WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
|
||||
/*factor_u8=*/fuzztest::Arbitrary<uint8_t>(),
|
||||
/*flip=*/fuzztest::Arbitrary<bool>(),
|
||||
/*bypass_filtering=*/fuzztest::Arbitrary<bool>(),
|
||||
/*no_fancy_upsampling=*/fuzztest::Arbitrary<bool>(),
|
||||
/*use_threads=*/fuzztest::Arbitrary<bool>(),
|
||||
/*use_cropping=*/fuzztest::Arbitrary<bool>(),
|
||||
/*use_scaling=*/fuzztest::Arbitrary<bool>(),
|
||||
/*use_dithering=*/fuzztest::Arbitrary<bool>(),
|
||||
#if defined(WEBP_REDUCE_CSP)
|
||||
fuzztest::ElementOf<int>({static_cast<int>(MODE_RGBA),
|
||||
static_cast<int>(MODE_BGRA),
|
||||
static_cast<int>(MODE_rgbA),
|
||||
static_cast<int>(MODE_bgrA)}),
|
||||
#else
|
||||
fuzztest::InRange<int>(0, static_cast<int>(MODE_LAST) - 1),
|
||||
#endif
|
||||
/*incremental=*/fuzztest::Arbitrary<bool>());
|
@ -14,37 +14,46 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/mux_types.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
namespace {
|
||||
|
||||
void AnimationApiTest(std::string_view blob, bool use_threads,
|
||||
WEBP_CSP_MODE color_mode) {
|
||||
const size_t size = blob.size();
|
||||
WebPData webp_data;
|
||||
WebPDataInit(&webp_data);
|
||||
webp_data.size = size;
|
||||
webp_data.bytes = data;
|
||||
webp_data.bytes = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
|
||||
// WebPAnimDecoderNew uses WebPDemux internally to calloc canvas size.
|
||||
WebPDemuxer* const demux = WebPDemux(&webp_data);
|
||||
if (!demux) return 0;
|
||||
if (!demux) return;
|
||||
const uint32_t cw = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
|
||||
const uint32_t ch = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
|
||||
if ((size_t)cw * ch > kFuzzPxLimit) {
|
||||
if ((size_t)cw * ch > fuzz_utils::kFuzzPxLimit) {
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// In addition to canvas size, check each frame separately.
|
||||
WebPIterator iter;
|
||||
for (int i = 0; i < kFuzzFrameLimit; i++) {
|
||||
for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; 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 > kFuzzPxLimit) { // image size of the frame payload
|
||||
if ((size_t)w * h >
|
||||
fuzz_utils::kFuzzPxLimit) { // image size of the frame payload
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,26 +62,30 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
WebPDemuxDelete(demux);
|
||||
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
if (!WebPAnimDecoderOptionsInit(&dec_options)) return 0;
|
||||
if (!WebPAnimDecoderOptionsInit(&dec_options)) return;
|
||||
|
||||
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;
|
||||
}
|
||||
dec_options.use_threads = use_threads;
|
||||
dec_options.color_mode = color_mode;
|
||||
|
||||
WebPAnimDecoder* dec = WebPAnimDecoderNew(&webp_data, &dec_options);
|
||||
if (!dec) return 0;
|
||||
if (!dec) return;
|
||||
|
||||
for (int i = 0; i < kFuzzFrameLimit; i++) {
|
||||
for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; i++) {
|
||||
uint8_t* buf;
|
||||
int timestamp;
|
||||
if (!WebPAnimDecoderGetNext(dec, &buf, ×tamp)) break;
|
||||
}
|
||||
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(AnimationApi, AnimationApiTest)
|
||||
.WithDomains(
|
||||
fuzztest::String()
|
||||
.WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
|
||||
/*use_threads=*/fuzztest::Arbitrary<bool>(),
|
||||
// Animations only support 4 (out of 12) modes.
|
||||
fuzztest::ElementOf<WEBP_CSP_MODE>({MODE_RGBA, MODE_BGRA, MODE_rgbA,
|
||||
MODE_bgrA}));
|
@ -16,13 +16,20 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "imageio/imageio_util.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/mux_types.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
namespace {
|
||||
|
||||
void AnimDecoderTest(std::string_view blob) {
|
||||
const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
const size_t size = blob.size();
|
||||
|
||||
// WebPAnimDecoderGetInfo() is too late to check the canvas size as
|
||||
// WebPAnimDecoderNew() will handle the allocations.
|
||||
const size_t kMaxNumBytes = 2684354560; // RSS (resident set size) limit.
|
||||
@ -34,14 +41,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
features.height) ||
|
||||
static_cast<size_t>(features.width) * features.height >
|
||||
kMaxNumPixelsSafe) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// decode everything as an animation
|
||||
WebPData webp_data = {data, size};
|
||||
WebPAnimDecoder* const dec = WebPAnimDecoderNew(&webp_data, nullptr);
|
||||
if (dec == nullptr) return 0;
|
||||
if (dec == nullptr) return;
|
||||
|
||||
WebPAnimInfo info;
|
||||
if (!WebPAnimDecoderGetInfo(dec, &info)) goto End;
|
||||
@ -57,5 +64,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
}
|
||||
End:
|
||||
WebPAnimDecoderDelete(dec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(AnimDecoder, AnimDecoderTest)
|
||||
.WithDomains(
|
||||
fuzztest::String()
|
||||
.WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
|
||||
|
@ -14,21 +14,48 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/mux.h"
|
||||
#include "src/webp/mux_types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo;
|
||||
const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
|
||||
|
||||
struct FrameConfig {
|
||||
int use_argb;
|
||||
int timestamp;
|
||||
WebPConfig webp_config;
|
||||
fuzz_utils::CropOrScaleParams crop_or_scale_params;
|
||||
int source_image_index;
|
||||
};
|
||||
|
||||
auto ArbitraryKMinKMax() {
|
||||
return fuzztest::FlatMap(
|
||||
[](int kmax) {
|
||||
const int min_kmin = (kmax > 1) ? (kmax / 2) : 0;
|
||||
const int max_kmin = (kmax > 1) ? (kmax - 1) : 0;
|
||||
return fuzztest::PairOf(fuzztest::InRange(min_kmin, max_kmin),
|
||||
fuzztest::Just(kmax));
|
||||
},
|
||||
fuzztest::InRange(0, 15));
|
||||
}
|
||||
|
||||
int AddFrame(WebPAnimEncoder** const enc,
|
||||
const WebPAnimEncoderOptions& anim_config, int* const width,
|
||||
int* const height, int timestamp_ms, const uint8_t data[],
|
||||
size_t size, uint32_t* const bit_pos) {
|
||||
int* const height, int timestamp_ms,
|
||||
const FrameConfig& frame_config, const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
if (enc == nullptr || width == nullptr || height == nullptr) {
|
||||
fprintf(stderr, "NULL parameters.\n");
|
||||
if (enc != nullptr) WebPAnimEncoderDelete(*enc);
|
||||
@ -36,27 +63,12 @@ int AddFrame(WebPAnimEncoder** const enc,
|
||||
}
|
||||
|
||||
// Init the source picture.
|
||||
WebPPicture pic;
|
||||
if (!WebPPictureInit(&pic)) {
|
||||
fprintf(stderr, "WebPPictureInit failed.\n");
|
||||
WebPAnimEncoderDelete(*enc);
|
||||
abort();
|
||||
}
|
||||
pic.use_argb = Extract(1, data, size, bit_pos);
|
||||
|
||||
// Read the source picture.
|
||||
if (!ExtractSourcePicture(&pic, data, size, bit_pos)) {
|
||||
const WebPEncodingError error_code = pic.error_code;
|
||||
WebPAnimEncoderDelete(*enc);
|
||||
WebPPictureFree(&pic);
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
|
||||
fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
|
||||
abort();
|
||||
}
|
||||
WebPPicture pic = fuzz_utils::GetSourcePicture(
|
||||
frame_config.source_image_index, frame_config.use_argb);
|
||||
|
||||
// Crop and scale.
|
||||
if (*enc == nullptr) { // First frame will set canvas width and height.
|
||||
if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) {
|
||||
if (!fuzz_utils::CropOrScale(&pic, frame_config.crop_or_scale_params)) {
|
||||
const WebPEncodingError error_code = pic.error_code;
|
||||
WebPPictureFree(&pic);
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
|
||||
@ -89,13 +101,7 @@ int AddFrame(WebPAnimEncoder** const enc,
|
||||
}
|
||||
|
||||
// Create frame encoding config.
|
||||
WebPConfig config;
|
||||
if (!ExtractWebPConfig(&config, data, size, bit_pos)) {
|
||||
fprintf(stderr, "ExtractWebPConfig failed.\n");
|
||||
WebPAnimEncoderDelete(*enc);
|
||||
WebPPictureFree(&pic);
|
||||
abort();
|
||||
}
|
||||
WebPConfig config = frame_config.webp_config;
|
||||
// Skip slow settings on big images, it's likely to timeout.
|
||||
if (pic.width * pic.height > 32 * 32) {
|
||||
config.method = (config.method > 4) ? 4 : config.method;
|
||||
@ -125,14 +131,17 @@ int AddFrame(WebPAnimEncoder** const enc,
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
void AnimEncoderTest(std::string_view blob, bool minimize_size,
|
||||
std::pair<int, int> kmin_kmax, bool allow_mixed,
|
||||
const std::vector<FrameConfig>& frame_configs,
|
||||
int optimization_index) {
|
||||
WebPAnimEncoder* enc = nullptr;
|
||||
int width = 0, height = 0, timestamp_ms = 0;
|
||||
uint32_t bit_pos = 0;
|
||||
const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
const size_t size = blob.size();
|
||||
|
||||
ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos);
|
||||
fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
|
||||
|
||||
// Extract a configuration from the packed bits.
|
||||
WebPAnimEncoderOptions anim_config;
|
||||
@ -140,26 +149,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n");
|
||||
abort();
|
||||
}
|
||||
anim_config.minimize_size = Extract(1, data, size, &bit_pos);
|
||||
anim_config.kmax = Extract(15, data, size, &bit_pos);
|
||||
const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0;
|
||||
const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0;
|
||||
anim_config.kmin =
|
||||
min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos);
|
||||
anim_config.allow_mixed = Extract(1, data, size, &bit_pos);
|
||||
anim_config.minimize_size = minimize_size;
|
||||
anim_config.kmin = kmin_kmax.first;
|
||||
anim_config.kmax = kmin_kmax.second;
|
||||
anim_config.allow_mixed = allow_mixed;
|
||||
anim_config.verbose = 0;
|
||||
|
||||
const int nb_frames = 1 + Extract(15, data, size, &bit_pos);
|
||||
|
||||
// For each frame.
|
||||
for (int i = 0; i < nb_frames; ++i) {
|
||||
if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size,
|
||||
&bit_pos)) {
|
||||
return 0;
|
||||
for (const FrameConfig& frame_config : frame_configs) {
|
||||
if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms,
|
||||
frame_config, data, size, &bit_pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) +
|
||||
Extract(1, data, size, &bit_pos); // [1..131073], arbitrary
|
||||
timestamp_ms += frame_config.timestamp;
|
||||
}
|
||||
|
||||
// Assemble.
|
||||
@ -184,5 +187,22 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
|
||||
WebPAnimEncoderDelete(enc);
|
||||
WebPDataClear(&webp_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(AnimEncoder, AnimEncoderTest)
|
||||
.WithDomains(
|
||||
fuzztest::String(),
|
||||
/*minimize_size=*/fuzztest::Arbitrary<bool>(), ArbitraryKMinKMax(),
|
||||
/*allow_mixed=*/fuzztest::Arbitrary<bool>(),
|
||||
fuzztest::VectorOf(
|
||||
fuzztest::StructOf<FrameConfig>(
|
||||
fuzztest::InRange<int>(0, 1), fuzztest::InRange<int>(0, 131073),
|
||||
fuzz_utils::ArbitraryWebPConfig(),
|
||||
fuzz_utils::ArbitraryCropOrScaleParams(),
|
||||
fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1)))
|
||||
.WithMinSize(1)
|
||||
.WithMaxSize(15),
|
||||
/*optimization_index=*/
|
||||
fuzztest::InRange<uint32_t>(0, fuzz_utils::kMaxOptimizationIndex));
|
||||
|
@ -14,57 +14,37 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/encode.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo;
|
||||
const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
uint32_t bit_pos = 0;
|
||||
|
||||
ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos);
|
||||
void EncDecTest(bool use_argb, int source_image_index, WebPConfig config,
|
||||
int optimization_index,
|
||||
const fuzz_utils::CropOrScaleParams& crop_or_scale_params) {
|
||||
fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
|
||||
|
||||
// Init the source picture.
|
||||
WebPPicture pic;
|
||||
if (!WebPPictureInit(&pic)) {
|
||||
fprintf(stderr, "WebPPictureInit failed.\n");
|
||||
abort();
|
||||
}
|
||||
pic.use_argb = Extract(1, data, size, &bit_pos);
|
||||
|
||||
// Read the source picture.
|
||||
if (!ExtractSourcePicture(&pic, data, size, &bit_pos)) {
|
||||
const WebPEncodingError error_code = pic.error_code;
|
||||
WebPPictureFree(&pic);
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
|
||||
fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
|
||||
abort();
|
||||
}
|
||||
WebPPicture pic = fuzz_utils::GetSourcePicture(source_image_index, use_argb);
|
||||
|
||||
// Crop and scale.
|
||||
if (!ExtractAndCropOrScale(&pic, data, size, &bit_pos)) {
|
||||
if (!fuzz_utils::CropOrScale(&pic, crop_or_scale_params)) {
|
||||
const WebPEncodingError error_code = pic.error_code;
|
||||
WebPPictureFree(&pic);
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return;
|
||||
fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
|
||||
error_code);
|
||||
abort();
|
||||
}
|
||||
|
||||
// Extract a configuration from the packed bits.
|
||||
WebPConfig config;
|
||||
if (!ExtractWebPConfig(&config, data, size, &bit_pos)) {
|
||||
fprintf(stderr, "ExtractWebPConfig failed.\n");
|
||||
abort();
|
||||
}
|
||||
// Skip slow settings on big images, it's likely to timeout.
|
||||
if (pic.width * pic.height > 32 * 32) {
|
||||
if (config.lossless) {
|
||||
@ -93,7 +73,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
WebPPictureFree(&pic);
|
||||
if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY ||
|
||||
error_code == VP8_ENC_ERROR_BAD_WRITE) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
|
||||
abort();
|
||||
@ -157,5 +137,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
WebPFreeDecBuffer(&dec_config.output);
|
||||
WebPMemoryWriterClear(&memory_writer);
|
||||
WebPPictureFree(&pic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(EncDec, EncDecTest)
|
||||
.WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
|
||||
/*source_image_index=*/
|
||||
fuzztest::InRange<int>(0, fuzz_utils::kNumSourceImages - 1),
|
||||
fuzz_utils::ArbitraryWebPConfig(),
|
||||
/*optimization_index=*/
|
||||
fuzztest::InRange<uint32_t>(0,
|
||||
fuzz_utils::kMaxOptimizationIndex),
|
||||
fuzz_utils::ArbitraryCropOrScaleParams());
|
||||
|
201
tests/fuzzer/fuzz_utils.cc
Normal file
201
tests/fuzzer/fuzz_utils.cc
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "./img_alpha.h"
|
||||
#include "./img_grid.h"
|
||||
#include "./img_peak.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/decode.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
namespace fuzz_utils {
|
||||
|
||||
WebPPicture GetSourcePicture(int image_index, bool use_argb) {
|
||||
WebPPicture pic;
|
||||
if (!WebPPictureInit(&pic)) abort();
|
||||
pic.use_argb = use_argb;
|
||||
|
||||
// Pick a source picture.
|
||||
const int kImagesWidth[] = {kImgAlphaWidth, kImgGridWidth, kImgPeakWidth};
|
||||
const int kImagesHeight[] = {kImgAlphaHeight, kImgGridHeight, kImgPeakHeight};
|
||||
const uint8_t* const image_data = kImagesData[image_index];
|
||||
pic.width = kImagesWidth[image_index];
|
||||
pic.height = kImagesHeight[image_index];
|
||||
pic.argb_stride = pic.width * 4 * sizeof(uint8_t);
|
||||
|
||||
// Read the bytes.
|
||||
if (!WebPPictureImportRGBA(&pic, image_data, pic.argb_stride)) abort();
|
||||
return pic;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int CropOrScale(WebPPicture* const pic, const CropOrScaleParams& params) {
|
||||
if (pic == NULL) return 0;
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
if (params.alter_input) {
|
||||
if (params.crop_or_scale) {
|
||||
const int cropped_width = std::max(1, pic->width / params.width_ratio);
|
||||
const int cropped_height = std::max(1, pic->height / params.height_ratio);
|
||||
const int cropped_left = (pic->width - cropped_width) / params.left_ratio;
|
||||
const int cropped_top = (pic->height - cropped_height) / params.top_ratio;
|
||||
return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width,
|
||||
cropped_height);
|
||||
} else {
|
||||
const int scaled_width = 1 + (pic->width * params.width_ratio) / 8;
|
||||
const int scaled_height = 1 + (pic->height * params.height_ratio) / 8;
|
||||
return WebPPictureRescale(pic, scaled_width, scaled_height);
|
||||
}
|
||||
}
|
||||
#else // defined(WEBP_REDUCE_SIZE)
|
||||
(void)data;
|
||||
(void)size;
|
||||
(void)bit_pos;
|
||||
#endif // !defined(WEBP_REDUCE_SIZE)
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" VP8CPUInfo VP8GetCPUInfo;
|
||||
static VP8CPUInfo GetCPUInfo;
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) {
|
||||
if (feature == kSSE4_1 || feature == kAVX) return 0;
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) {
|
||||
if (feature == kAVX) return 0;
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) {
|
||||
if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) {
|
||||
return 1; // we have SSE3 -> force SlowSSSE3
|
||||
}
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) {
|
||||
(void)feature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetOptimization(VP8CPUInfo default_VP8GetCPUInfo, uint32_t index) {
|
||||
assert(index <= kMaxOptimizationIndex);
|
||||
GetCPUInfo = default_VP8GetCPUInfo;
|
||||
const VP8CPUInfo kVP8CPUInfos[kMaxOptimizationIndex + 1] = {
|
||||
GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, GetCPUInfoNoSSE41,
|
||||
GetCPUInfoNoAVX, GetCPUInfo};
|
||||
VP8GetCPUInfo = kVP8CPUInfos[index];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::vector<std::string> ReadFilesFromDirectory(std::string_view dir) {
|
||||
std::vector<std::tuple<std::string>> tuples =
|
||||
fuzztest::ReadFilesFromDirectory(dir);
|
||||
std::vector<std::string> strings(tuples.size());
|
||||
for (size_t i = 0; i < tuples.size(); ++i) {
|
||||
using std::swap;
|
||||
swap(std::get<0>(tuples[i]), strings[i]);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// The code in this section is copied from
|
||||
// https://github.com/webmproject/sjpeg/blob/
|
||||
// 1c025b3dbc2246de3e1d7c287970f1a01291800f/src/jpeg_tools.cc#L47
|
||||
// (same license as this file).
|
||||
|
||||
namespace {
|
||||
// Constants below are marker codes defined in JPEG spec
|
||||
// ISO/IEC 10918-1 : 1993(E) Table B.1
|
||||
// See also: http://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
||||
|
||||
#define M_SOF0 0xffc0
|
||||
#define M_SOF1 0xffc1
|
||||
|
||||
const uint8_t* GetSOFData(const uint8_t* src, int size) {
|
||||
if (src == NULL) return NULL;
|
||||
const uint8_t* const end = src + size - 8; // 8 bytes of safety, for marker
|
||||
src += 2; // skip M_SOI
|
||||
for (; src < end && *src != 0xff; ++src) { /* search first 0xff marker */
|
||||
}
|
||||
while (src < end) {
|
||||
const uint32_t marker = static_cast<uint32_t>((src[0] << 8) | src[1]);
|
||||
if (marker == M_SOF0 || marker == M_SOF1) return src;
|
||||
const size_t s = 2 + ((src[2] << 8) | src[3]);
|
||||
src += s;
|
||||
}
|
||||
return NULL; // No SOF marker found
|
||||
}
|
||||
|
||||
bool SjpegDimensions(const uint8_t* src0, size_t size, int* width, int* height,
|
||||
int* is_yuv420) {
|
||||
if (width == NULL || height == NULL) return false;
|
||||
const uint8_t* src = GetSOFData(src0, size);
|
||||
const size_t left_over = size - (src - src0);
|
||||
if (src == NULL || left_over < 8 + 3 * 1) return false;
|
||||
if (height != NULL) *height = (src[5] << 8) | src[6];
|
||||
if (width != NULL) *width = (src[7] << 8) | src[8];
|
||||
if (is_yuv420 != NULL) {
|
||||
const size_t nb_comps = src[9];
|
||||
*is_yuv420 = (nb_comps == 3);
|
||||
if (left_over < 11 + 3 * nb_comps) return false;
|
||||
for (int c = 0; *is_yuv420 && c < 3; ++c) {
|
||||
const int expected_dim = (c == 0 ? 0x22 : 0x11);
|
||||
*is_yuv420 &= (src[11 + c * 3] == expected_dim);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool IsImageTooBig(const uint8_t* data, size_t size) {
|
||||
int width, height, components;
|
||||
if (SjpegDimensions(data, size, &width, &height, &components) ||
|
||||
WebPGetInfo(data, size, &width, &height)) {
|
||||
// Look at the number of 8x8px blocks rather than the overall pixel count
|
||||
// when comparing to memory and duration thresholds.
|
||||
const size_t ceiled_width = ((size_t)width + 7) / 8 * 8;
|
||||
const size_t ceiled_height = ((size_t)height + 7) / 8 * 8;
|
||||
// Threshold to avoid out-of-memory and timeout issues.
|
||||
// The threshold is arbitrary but below the fuzzer limit of 2 GB.
|
||||
// The value cannot be 2 GB because of the added memory by MSAN.
|
||||
if (ceiled_width * ceiled_height > kFuzzPxLimit) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace fuzz_utils
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
// Copyright 2018-2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,14 +17,23 @@
|
||||
#ifndef WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
|
||||
#define WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "./img_alpha.h"
|
||||
#include "./img_grid.h"
|
||||
#include "./img_peak.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/webp/encode.h"
|
||||
#include "fuzztest/fuzztest.h"
|
||||
|
||||
namespace fuzz_utils {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Arbitrary limits to prevent OOM, timeout, or slow execution.
|
||||
@ -54,170 +63,139 @@ static WEBP_INLINE uint8_t FuzzHash(const uint8_t* const data, size_t size) {
|
||||
return value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Extract an integer in [0, max_value].
|
||||
|
||||
static WEBP_INLINE uint32_t Extract(uint32_t max_value,
|
||||
const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
uint32_t v = 0;
|
||||
uint32_t range = 1;
|
||||
while (*bit_pos < 8 * size && range <= max_value) {
|
||||
const uint8_t mask = 1u << (*bit_pos & 7);
|
||||
v = (v << 1) | !!(data[*bit_pos >> 3] & mask);
|
||||
range <<= 1;
|
||||
++*bit_pos;
|
||||
}
|
||||
return v % (max_value + 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Some functions to override VP8GetCPUInfo and disable some optimizations.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" VP8CPUInfo VP8GetCPUInfo;
|
||||
#else
|
||||
extern VP8CPUInfo VP8GetCPUInfo;
|
||||
#endif
|
||||
static VP8CPUInfo GetCPUInfo;
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) {
|
||||
if (feature == kSSE4_1 || feature == kAVX) return 0;
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) {
|
||||
if (feature == kAVX) return 0;
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) {
|
||||
if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) {
|
||||
return 1; // we have SSE3 -> force SlowSSSE3
|
||||
}
|
||||
return GetCPUInfo(feature);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) {
|
||||
(void)feature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void ExtractAndDisableOptimizations(
|
||||
VP8CPUInfo default_VP8GetCPUInfo, const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
GetCPUInfo = default_VP8GetCPUInfo;
|
||||
const VP8CPUInfo kVP8CPUInfos[5] = {GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3,
|
||||
GetCPUInfoNoSSE41, GetCPUInfoNoAVX,
|
||||
GetCPUInfo};
|
||||
int VP8GetCPUInfo_index = Extract(4, data, size, bit_pos);
|
||||
VP8GetCPUInfo = kVP8CPUInfos[VP8GetCPUInfo_index];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static WEBP_INLINE int ExtractWebPConfig(WebPConfig* const config,
|
||||
const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
if (config == NULL || !WebPConfigInit(config)) return 0;
|
||||
config->lossless = Extract(1, data, size, bit_pos);
|
||||
config->quality = Extract(100, data, size, bit_pos);
|
||||
config->method = Extract(6, data, size, bit_pos);
|
||||
config->image_hint =
|
||||
(WebPImageHint)Extract(WEBP_HINT_LAST - 1, data, size, bit_pos);
|
||||
config->segments = 1 + Extract(3, data, size, bit_pos);
|
||||
config->sns_strength = Extract(100, data, size, bit_pos);
|
||||
config->filter_strength = Extract(100, data, size, bit_pos);
|
||||
config->filter_sharpness = Extract(7, data, size, bit_pos);
|
||||
config->filter_type = Extract(1, data, size, bit_pos);
|
||||
config->autofilter = Extract(1, data, size, bit_pos);
|
||||
config->alpha_compression = Extract(1, data, size, bit_pos);
|
||||
config->alpha_filtering = Extract(2, data, size, bit_pos);
|
||||
config->alpha_quality = Extract(100, data, size, bit_pos);
|
||||
config->pass = 1 + Extract(9, data, size, bit_pos);
|
||||
config->show_compressed = 1;
|
||||
config->preprocessing = Extract(2, data, size, bit_pos);
|
||||
config->partitions = Extract(3, data, size, bit_pos);
|
||||
config->partition_limit = 10 * Extract(10, data, size, bit_pos);
|
||||
config->emulate_jpeg_size = Extract(1, data, size, bit_pos);
|
||||
config->thread_level = Extract(1, data, size, bit_pos);
|
||||
config->low_memory = Extract(1, data, size, bit_pos);
|
||||
config->near_lossless = 20 * Extract(5, data, size, bit_pos);
|
||||
config->exact = Extract(1, data, size, bit_pos);
|
||||
config->use_delta_palette = Extract(1, data, size, bit_pos);
|
||||
config->use_sharp_yuv = Extract(1, data, size, bit_pos);
|
||||
return WebPValidateConfig(config);
|
||||
constexpr const uint8_t* kImagesData[] = {kImgAlphaData, kImgGridData,
|
||||
kImgPeakData};
|
||||
constexpr size_t kNumSourceImages =
|
||||
sizeof(kImagesData) / sizeof(kImagesData[0]);
|
||||
|
||||
WebPPicture GetSourcePicture(int image_index, bool use_argb);
|
||||
|
||||
static inline auto ArbitraryWebPConfig() {
|
||||
return fuzztest::Map(
|
||||
[](int lossless, int quality, int method, int image_hint, int segments,
|
||||
int sns_strength, int filter_strength, int filter_sharpness,
|
||||
int filter_type, int autofilter, int alpha_compression,
|
||||
int alpha_filtering, int alpha_quality, int pass, int preprocessing,
|
||||
int partitions, int partition_limit, int emulate_jpeg_size,
|
||||
int thread_level, int low_memory, int near_lossless, int exact,
|
||||
int use_delta_palette, int use_sharp_yuv) -> WebPConfig {
|
||||
WebPConfig config;
|
||||
if (!WebPConfigInit(&config)) abort();
|
||||
config.lossless = lossless;
|
||||
config.quality = quality;
|
||||
config.method = method;
|
||||
config.image_hint = (WebPImageHint)image_hint;
|
||||
config.segments = segments;
|
||||
config.sns_strength = sns_strength;
|
||||
config.filter_strength = filter_strength;
|
||||
config.filter_sharpness = filter_sharpness;
|
||||
config.filter_type = filter_type;
|
||||
config.autofilter = autofilter;
|
||||
config.alpha_compression = alpha_compression;
|
||||
config.alpha_filtering = alpha_filtering;
|
||||
config.alpha_quality = alpha_quality;
|
||||
config.pass = pass;
|
||||
config.show_compressed = 1;
|
||||
config.preprocessing = preprocessing;
|
||||
config.partitions = partitions;
|
||||
config.partition_limit = 10 * partition_limit;
|
||||
config.emulate_jpeg_size = emulate_jpeg_size;
|
||||
config.thread_level = thread_level;
|
||||
config.low_memory = low_memory;
|
||||
config.near_lossless = 20 * near_lossless;
|
||||
config.exact = exact;
|
||||
config.use_delta_palette = use_delta_palette;
|
||||
config.use_sharp_yuv = use_sharp_yuv;
|
||||
if (!WebPValidateConfig(&config)) abort();
|
||||
return config;
|
||||
},
|
||||
/*lossless=*/fuzztest::InRange<int>(0, 1),
|
||||
/*quality=*/fuzztest::InRange<int>(0, 100),
|
||||
/*method=*/fuzztest::InRange<int>(0, 6),
|
||||
/*image_hint=*/fuzztest::InRange<int>(0, WEBP_HINT_LAST - 1),
|
||||
/*segments=*/fuzztest::InRange<int>(1, 4),
|
||||
/*sns_strength=*/fuzztest::InRange<int>(0, 100),
|
||||
/*filter_strength=*/fuzztest::InRange<int>(0, 100),
|
||||
/*filter_sharpness=*/fuzztest::InRange<int>(0, 7),
|
||||
/*filter_type=*/fuzztest::InRange<int>(0, 1),
|
||||
/*autofilter=*/fuzztest::InRange<int>(0, 1),
|
||||
/*alpha_compression=*/fuzztest::InRange<int>(0, 1),
|
||||
/*alpha_filtering=*/fuzztest::InRange<int>(0, 2),
|
||||
/*alpha_quality=*/fuzztest::InRange<int>(0, 100),
|
||||
/*pass=*/fuzztest::InRange<int>(1, 10),
|
||||
/*preprocessing=*/fuzztest::InRange<int>(0, 2),
|
||||
/*partitions=*/fuzztest::InRange<int>(0, 3),
|
||||
/*partition_limit=*/fuzztest::InRange<int>(0, 10),
|
||||
/*emulate_jpeg_size=*/fuzztest::InRange<int>(0, 1),
|
||||
/*thread_level=*/fuzztest::InRange<int>(0, 1),
|
||||
/*low_memory=*/fuzztest::InRange<int>(0, 1),
|
||||
/*near_lossless=*/fuzztest::InRange<int>(0, 5),
|
||||
/*exact=*/fuzztest::InRange<int>(0, 1),
|
||||
/*use_delta_palette=*/fuzztest::InRange<int>(0, 1),
|
||||
/*use_sharp_yuv=*/fuzztest::InRange<int>(0, 1));
|
||||
}
|
||||
|
||||
struct CropOrScaleParams {
|
||||
bool alter_input;
|
||||
bool crop_or_scale;
|
||||
int width_ratio;
|
||||
int height_ratio;
|
||||
int left_ratio;
|
||||
int top_ratio;
|
||||
};
|
||||
|
||||
static inline auto ArbitraryCropOrScaleParams() {
|
||||
return fuzztest::Map(
|
||||
[](const std::optional<std::pair<int, int>>& width_height_ratio,
|
||||
const std::optional<std::pair<int, int>>& left_top_ratio)
|
||||
-> CropOrScaleParams {
|
||||
CropOrScaleParams params;
|
||||
params.alter_input = width_height_ratio.has_value();
|
||||
if (params.alter_input) {
|
||||
params.width_ratio = width_height_ratio->first;
|
||||
params.height_ratio = width_height_ratio->second;
|
||||
params.crop_or_scale = left_top_ratio.has_value();
|
||||
if (params.crop_or_scale) {
|
||||
params.left_ratio = left_top_ratio->first;
|
||||
params.top_ratio = left_top_ratio->second;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
},
|
||||
fuzztest::OptionalOf(
|
||||
fuzztest::PairOf(fuzztest::InRange(1, 8), fuzztest::InRange(1, 8))),
|
||||
fuzztest::OptionalOf(
|
||||
fuzztest::PairOf(fuzztest::InRange(1, 8), fuzztest::InRange(1, 8))));
|
||||
}
|
||||
|
||||
// Crops or scales a picture according to the given params.
|
||||
int CropOrScale(WebPPicture* pic, const CropOrScaleParams& params);
|
||||
|
||||
// Imposes a level of optimization among one of the kMaxOptimizationIndex+1
|
||||
// possible values: OnlyC, ForceSlowSSSE3, NoSSE41, NoAVX, default.
|
||||
static constexpr uint32_t kMaxOptimizationIndex = 4;
|
||||
void SetOptimization(VP8CPUInfo default_VP8GetCPUInfo, uint32_t index);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static WEBP_INLINE int ExtractSourcePicture(WebPPicture* const pic,
|
||||
const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
if (pic == NULL) return 0;
|
||||
// See https://developers.google.com/speed/webp/docs/riff_container.
|
||||
static constexpr size_t kMaxWebPFileSize = (1ull << 32) - 2; // 4 GiB - 2
|
||||
|
||||
// Pick a source picture.
|
||||
const uint8_t* kImagesData[] = {
|
||||
kImgAlphaData,
|
||||
kImgGridData,
|
||||
kImgPeakData
|
||||
};
|
||||
const int kImagesWidth[] = {
|
||||
kImgAlphaWidth,
|
||||
kImgGridWidth,
|
||||
kImgPeakWidth
|
||||
};
|
||||
const int kImagesHeight[] = {
|
||||
kImgAlphaHeight,
|
||||
kImgGridHeight,
|
||||
kImgPeakHeight
|
||||
};
|
||||
const size_t kNbImages = sizeof(kImagesData) / sizeof(kImagesData[0]);
|
||||
const size_t image_index = Extract(kNbImages - 1, data, size, bit_pos);
|
||||
const uint8_t* const image_data = kImagesData[image_index];
|
||||
pic->width = kImagesWidth[image_index];
|
||||
pic->height = kImagesHeight[image_index];
|
||||
pic->argb_stride = pic->width * 4 * sizeof(uint8_t);
|
||||
std::vector<std::string> GetDictionaryFromFiles(
|
||||
const std::vector<std::string_view>& file_paths);
|
||||
|
||||
// Read the bytes.
|
||||
return WebPPictureImportRGBA(pic, image_data, pic->argb_stride);
|
||||
}
|
||||
// Checks whether the binary blob containing a JPEG or WebP is too big for the
|
||||
// fuzzer.
|
||||
bool IsImageTooBig(const uint8_t* data, size_t size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static WEBP_INLINE int Max(int a, int b) { return ((a < b) ? b : a); }
|
||||
|
||||
static WEBP_INLINE int ExtractAndCropOrScale(WebPPicture* const pic,
|
||||
const uint8_t data[], size_t size,
|
||||
uint32_t* const bit_pos) {
|
||||
if (pic == NULL) return 0;
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
const int alter_input = Extract(1, data, size, bit_pos);
|
||||
const int crop_or_scale = Extract(1, data, size, bit_pos);
|
||||
const int width_ratio = 1 + Extract(7, data, size, bit_pos);
|
||||
const int height_ratio = 1 + Extract(7, data, size, bit_pos);
|
||||
if (alter_input) {
|
||||
if (crop_or_scale) {
|
||||
const uint32_t left_ratio = 1 + Extract(7, data, size, bit_pos);
|
||||
const uint32_t top_ratio = 1 + Extract(7, data, size, bit_pos);
|
||||
const int cropped_width = Max(1, pic->width / width_ratio);
|
||||
const int cropped_height = Max(1, pic->height / height_ratio);
|
||||
const int cropped_left = (pic->width - cropped_width) / left_ratio;
|
||||
const int cropped_top = (pic->height - cropped_height) / top_ratio;
|
||||
return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width,
|
||||
cropped_height);
|
||||
} else {
|
||||
const int scaled_width = 1 + (pic->width * width_ratio) / 8;
|
||||
const int scaled_height = 1 + (pic->height * height_ratio) / 8;
|
||||
return WebPPictureRescale(pic, scaled_width, scaled_height);
|
||||
}
|
||||
}
|
||||
#else // defined(WEBP_REDUCE_SIZE)
|
||||
(void)data;
|
||||
(void)size;
|
||||
(void)bit_pos;
|
||||
#endif // !defined(WEBP_REDUCE_SIZE)
|
||||
return 1;
|
||||
}
|
||||
} // namespace fuzz_utils
|
||||
|
||||
#endif // WEBP_TESTS_FUZZER_FUZZ_UTILS_H_
|
||||
|
@ -14,22 +14,29 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/utils/bit_reader_utils.h"
|
||||
#include "src/utils/huffman_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
namespace {
|
||||
|
||||
void HuffmanTest(std::string_view blob) {
|
||||
const uint8_t* const data = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
const size_t size = blob.size();
|
||||
|
||||
// Number of bits to initialize data.
|
||||
static const int kColorCacheBitsBits = 4;
|
||||
// 'num_htree_groups' is contained in the RG channel, hence 16 bits.
|
||||
static const int kNumHtreeGroupsBits = 16;
|
||||
if (size * sizeof(*data) < kColorCacheBitsBits + kNumHtreeGroupsBits) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// A non-NULL mapping brings minor changes that are tested by the normal
|
||||
@ -39,27 +46,32 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
memset(&huffman_tables, 0, sizeof(huffman_tables));
|
||||
HTreeGroup* htree_groups = NULL;
|
||||
|
||||
int num_htree_groups, num_htree_groups_max, color_cache_bits;
|
||||
VP8LBitReader* br;
|
||||
VP8LDecoder* dec = VP8LNew();
|
||||
if (dec == NULL) goto Error;
|
||||
VP8LBitReader* const br = &dec->br_;
|
||||
br = &dec->br_;
|
||||
VP8LInitBitReader(br, data, size);
|
||||
|
||||
const int color_cache_bits = VP8LReadBits(br, kColorCacheBitsBits);
|
||||
color_cache_bits = VP8LReadBits(br, kColorCacheBitsBits);
|
||||
if (color_cache_bits < 1 || color_cache_bits > MAX_CACHE_BITS) goto Error;
|
||||
|
||||
const int num_htree_groups = VP8LReadBits(br, kNumHtreeGroupsBits);
|
||||
num_htree_groups = VP8LReadBits(br, kNumHtreeGroupsBits);
|
||||
// 'num_htree_groups' cannot be 0 as it is built from a non-empty image.
|
||||
if (num_htree_groups == 0) goto Error;
|
||||
if (num_htree_groups == 0) return;
|
||||
// This variable is only useful when mapping is not NULL.
|
||||
const int num_htree_groups_max = num_htree_groups;
|
||||
num_htree_groups_max = num_htree_groups;
|
||||
(void)ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups,
|
||||
num_htree_groups_max, mapping, dec,
|
||||
&huffman_tables, &htree_groups);
|
||||
|
||||
Error:
|
||||
Error:
|
||||
WebPSafeFree(mapping);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
VP8LHuffmanTablesDeallocate(&huffman_tables);
|
||||
VP8LDelete(dec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(Huffman, HuffmanTest).WithDomains(fuzztest::String());
|
@ -1,31 +0,0 @@
|
||||
# This Makefile will compile all fuzzing targets. It doesn't check tool
|
||||
# requirements and paths may need to be updated depending on your environment.
|
||||
# Note a clang 6+ toolchain is assumed for use of -fsanitize=fuzzer.
|
||||
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
CFLAGS = -fsanitize=fuzzer -I../../src -I../.. -Wall -Wextra
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
LDFLAGS = -fsanitize=fuzzer
|
||||
LDLIBS = ../../src/mux/libwebpmux.a ../../src/demux/libwebpdemux.a
|
||||
LDLIBS += ../../src/libwebp.a ../../imageio/libimageio_util.a
|
||||
LDLIBS += ../../sharpyuv/libsharpyuv.a
|
||||
|
||||
FUZZERS = advanced_api_fuzzer animation_api_fuzzer animdecoder_fuzzer
|
||||
FUZZERS += animencoder_fuzzer enc_dec_fuzzer huffman_fuzzer
|
||||
FUZZERS += mux_demux_api_fuzzer simple_api_fuzzer
|
||||
|
||||
%.o: fuzz_utils.h img_alpha.h img_grid.h img_peak.h
|
||||
all: $(FUZZERS)
|
||||
|
||||
define FUZZER_template
|
||||
$(1): $$(addsuffix .o, $(1)) $(LDLIBS)
|
||||
OBJS += $$(addsuffix .o, $(1))
|
||||
endef
|
||||
|
||||
$(foreach fuzzer, $(FUZZERS), $(eval $(call FUZZER_template, $(fuzzer))))
|
||||
|
||||
clean:
|
||||
$(RM) $(FUZZERS) $(OBJS)
|
||||
|
||||
.PHONY: all clean
|
@ -14,23 +14,30 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/webp/demux.h"
|
||||
#include "src/webp/mux.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
namespace {
|
||||
|
||||
void MuxDemuxApiTest(std::string_view data_in, bool mux) {
|
||||
const size_t size = data_in.size();
|
||||
WebPData webp_data;
|
||||
WebPDataInit(&webp_data);
|
||||
webp_data.size = size;
|
||||
webp_data.bytes = data;
|
||||
webp_data.bytes = reinterpret_cast<const uint8_t*>(data_in.data());
|
||||
|
||||
// Extracted chunks and frames are not processed or decoded,
|
||||
// which is already covered extensively by the other fuzz targets.
|
||||
|
||||
if (size & 1) {
|
||||
if (mux) {
|
||||
// Mux API
|
||||
WebPMux* mux = WebPMuxCreate(&webp_data, size & 2);
|
||||
if (!mux) return 0;
|
||||
if (!mux) return;
|
||||
|
||||
WebPData chunk;
|
||||
(void)WebPMuxGetChunk(mux, "EXIF", &chunk);
|
||||
@ -45,7 +52,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
|
||||
WebPMuxError status;
|
||||
WebPMuxFrameInfo info;
|
||||
for (int i = 0; i < kFuzzFrameLimit; i++) {
|
||||
for (int i = 0; i < fuzz_utils::kFuzzFrameLimit; i++) {
|
||||
status = WebPMuxGetFrame(mux, i + 1, &info);
|
||||
if (status == WEBP_MUX_NOT_FOUND) {
|
||||
break;
|
||||
@ -63,11 +70,11 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
demux = WebPDemuxPartial(&webp_data, &state);
|
||||
if (state < WEBP_DEMUX_PARSED_HEADER) {
|
||||
WebPDemuxDelete(demux);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
demux = WebPDemux(&webp_data);
|
||||
if (!demux) return 0;
|
||||
if (!demux) return;
|
||||
}
|
||||
|
||||
WebPChunkIterator chunk_iter;
|
||||
@ -83,7 +90,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
|
||||
WebPIterator iter;
|
||||
if (WebPDemuxGetFrame(demux, 1, &iter)) {
|
||||
for (int i = 1; i < kFuzzFrameLimit; i++) {
|
||||
for (int i = 1; i < fuzz_utils::kFuzzFrameLimit; i++) {
|
||||
if (!WebPDemuxNextFrame(&iter)) break;
|
||||
}
|
||||
}
|
||||
@ -91,6 +98,12 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demux);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(MuxDemuxApi, MuxDemuxApiTest)
|
||||
.WithDomains(
|
||||
fuzztest::String()
|
||||
.WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1),
|
||||
/*mux=*/fuzztest::Arbitrary<bool>());
|
@ -15,61 +15,69 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# This script is meant to be run by the oss-fuzz infrastructure from the script
|
||||
# https://github.com/google/oss-fuzz/blob/master/projects/libwebp/build.sh
|
||||
# It builds the different fuzz targets.
|
||||
|
||||
# To test changes to this file:
|
||||
# - make changes and commit to your REPO
|
||||
# - run:
|
||||
# git clone --depth=1 git@github.com:google/oss-fuzz.git
|
||||
# cd oss-fuzz
|
||||
# - modify projects/libwebp/Dockerfile to point to your REPO
|
||||
# - run:
|
||||
# python3 infra/helper.py build_image libwebp
|
||||
# # enter 'y' and wait for everything to be downloaded
|
||||
# - run:
|
||||
# python3 infra/helper.py build_fuzzers --sanitizer address libwebp
|
||||
# # wait for the tests to be built
|
||||
# And then run the fuzzer locally, for example:
|
||||
# python3 infra/helper.py run_fuzzer libwebp \
|
||||
# --sanitizer address \
|
||||
# animencoder_fuzzer__AnimEncoder.AnimEncoderTest
|
||||
|
||||
set -eu
|
||||
|
||||
# limit allocation size to reduce spurious OOMs
|
||||
WEBP_CFLAGS="$CFLAGS -DWEBP_MAX_IMAGE_SIZE=838860800" # 800MiB
|
||||
|
||||
./autogen.sh
|
||||
CFLAGS="$WEBP_CFLAGS" ./configure \
|
||||
--enable-asserts \
|
||||
--enable-libwebpdemux \
|
||||
--enable-libwebpmux \
|
||||
--disable-shared \
|
||||
--disable-jpeg \
|
||||
--disable-tiff \
|
||||
--disable-gif \
|
||||
--disable-wic
|
||||
make clean
|
||||
make -j$(nproc)
|
||||
export CFLAGS="$WEBP_CFLAGS"
|
||||
cmake -S . -B build -DWEBP_BUILD_FUZZTEST=ON
|
||||
cd build && make -j$(nproc) && cd ..
|
||||
|
||||
find $SRC/libwebp-test-data -type f -size -32k -iname "*.webp" \
|
||||
-exec zip -qju fuzz_seed_corpus.zip "{}" \;
|
||||
|
||||
webp_libs=(
|
||||
src/demux/.libs/libwebpdemux.a
|
||||
src/mux/.libs/libwebpmux.a
|
||||
src/.libs/libwebp.a
|
||||
imageio/.libs/libimageio_util.a
|
||||
sharpyuv/.libs/libsharpyuv.a
|
||||
)
|
||||
webp_c_fuzzers=(
|
||||
advanced_api_fuzzer
|
||||
animation_api_fuzzer
|
||||
huffman_fuzzer
|
||||
mux_demux_api_fuzzer
|
||||
simple_api_fuzzer
|
||||
)
|
||||
webp_cxx_fuzzers=(
|
||||
animdecoder_fuzzer
|
||||
animencoder_fuzzer
|
||||
enc_dec_fuzzer
|
||||
)
|
||||
|
||||
for fuzzer in "${webp_c_fuzzers[@]}"; do
|
||||
$CC $CFLAGS -Isrc -I. tests/fuzzer/${fuzzer}.c -c -o tests/fuzzer/${fuzzer}.o
|
||||
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE \
|
||||
tests/fuzzer/${fuzzer}.o -o $OUT/${fuzzer} \
|
||||
"${webp_libs[@]}"
|
||||
done
|
||||
|
||||
for fuzzer in "${webp_cxx_fuzzers[@]}"; do
|
||||
$CXX $CXXFLAGS -Isrc -I. $LIB_FUZZING_ENGINE \
|
||||
tests/fuzzer/${fuzzer}.cc -o $OUT/${fuzzer} \
|
||||
"${webp_libs[@]}"
|
||||
done
|
||||
|
||||
for fuzzer in "${webp_c_fuzzers[@]}" "${webp_cxx_fuzzers[@]}"; do
|
||||
cp fuzz_seed_corpus.zip $OUT/${fuzzer}_seed_corpus.zip
|
||||
cp tests/fuzzer/fuzz.dict $OUT/${fuzzer}.dict
|
||||
done
|
||||
# Restrict fuzztest tests to the only compatible fuzz engine: libfuzzer.
|
||||
if [[ "$FUZZING_ENGINE" == "libfuzzer" ]]; then
|
||||
# build fuzztests
|
||||
# The following is taken from https://github.com/google/oss-fuzz/blob/31ac7244748ea7390015455fb034b1f4eda039d9/infra/base-images/base-builder/compile_fuzztests.sh#L59
|
||||
# Iterate the fuzz binaries and list each fuzz entrypoint in the binary. For
|
||||
# each entrypoint create a wrapper script that calls into the binaries the
|
||||
# given entrypoint as argument.
|
||||
# The scripts will be named:
|
||||
# {binary_name}@{fuzztest_entrypoint}
|
||||
FUZZ_TEST_BINARIES_OUT_PATHS=$(find ./build/tests/fuzzer/ -executable -type f)
|
||||
echo "Fuzz binaries: $FUZZ_TEST_BINARIES_OUT_PATHS"
|
||||
for fuzz_main_file in $FUZZ_TEST_BINARIES_OUT_PATHS; do
|
||||
FUZZ_TESTS=$($fuzz_main_file --list_fuzz_tests | cut -d ' ' -f 4)
|
||||
cp -f ${fuzz_main_file} $OUT/
|
||||
fuzz_basename=$(basename $fuzz_main_file)
|
||||
chmod -x $OUT/$fuzz_basename
|
||||
for fuzz_entrypoint in $FUZZ_TESTS; do
|
||||
TARGET_FUZZER="${fuzz_basename}@$fuzz_entrypoint"
|
||||
# Write executer script
|
||||
echo "#!/bin/sh
|
||||
# LLVMFuzzerTestOneInput for fuzzer detection.
|
||||
this_dir=\$(dirname \"\$0\")
|
||||
export TEST_DATA_DIRS=\$this_dir/corpus
|
||||
chmod +x \$this_dir/$fuzz_basename
|
||||
\$this_dir/$fuzz_basename --fuzz=$fuzz_entrypoint -- \$@
|
||||
chmod -x \$this_dir/$fuzz_basename" > $OUT/$TARGET_FUZZER
|
||||
chmod +x $OUT/$TARGET_FUZZER
|
||||
done
|
||||
# Copy data.
|
||||
cp fuzz_seed_corpus.zip $OUT/${fuzz_basename}_seed_corpus.zip
|
||||
cp tests/fuzzer/fuzz.dict $OUT/${fuzz_basename}.dict
|
||||
done
|
||||
fi
|
||||
|
10
tests/fuzzer/patch.sh
Executable file
10
tests/fuzzer/patch.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# Fixes for https://github.com/google/fuzztest/issues/1124
|
||||
sed -i -e "s/-fsanitize=address//g" -e "s/-DADDRESS_SANITIZER//g" \
|
||||
./cmake/FuzzTestFlagSetup.cmake
|
||||
# Fixes for https://github.com/google/fuzztest/issues/1125
|
||||
before="if (IsEnginePlaceholderInput(data)) return;"
|
||||
after="if (data.size() == 0) return;"
|
||||
sed -i "s/${before}/${after}/" ./fuzztest/internal/compatibility_mode.cc
|
||||
sed -i "s/set(GTEST_HAS_ABSL ON)/set(GTEST_HAS_ABSL OFF)/" \
|
||||
./cmake/BuildDependencies.cmake
|
@ -14,15 +14,23 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#include "./fuzz_utils.h"
|
||||
#include "src/webp/decode.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
int w, h;
|
||||
if (!WebPGetInfo(data, size, &w, &h)) return 0;
|
||||
if ((size_t)w * h > kFuzzPxLimit) return 0;
|
||||
namespace {
|
||||
|
||||
const uint8_t value = FuzzHash(data, size);
|
||||
void SimpleApiTest(std::string_view data_in) {
|
||||
const uint8_t* const data = reinterpret_cast<const uint8_t*>(data_in.data());
|
||||
const size_t size = data_in.size();
|
||||
int w, h;
|
||||
if (!WebPGetInfo(data, size, &w, &h)) return;
|
||||
if ((size_t)w * h > fuzz_utils::kFuzzPxLimit) return;
|
||||
|
||||
const uint8_t value = fuzz_utils::FuzzHash(data, size);
|
||||
uint8_t* buf = NULL;
|
||||
|
||||
// For *Into functions, which decode into an external buffer, an
|
||||
@ -84,6 +92,11 @@ int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
|
||||
}
|
||||
|
||||
if (buf) WebPFree(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FUZZ_TEST(SimpleApi, SimpleApiTest)
|
||||
.WithDomains(
|
||||
fuzztest::String()
|
||||
.WithMaxSize(fuzz_utils::kMaxWebPFileSize + 1));
|
Loading…
Reference in New Issue
Block a user