diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml deleted file mode 100644 index c03abaaac6..0000000000 --- a/.github/workflows/cifuzz.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CI Fuzz -on: - push: - pull_request: - paths: - - '.github/workflows/cifuzz.yml' - - '**CMakeLists.txt' - - 'cmake/**' - - 'ext/**' - - 'tests/gtest/**' - - 'tests/oss-fuzz/**' - -permissions: - contents: read - -# Cancel the workflow if a new one is triggered from the same PR, branch, or tag, except on main. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -jobs: - fuzz: - runs-on: ubuntu-latest - steps: - - name: Build Fuzzers - id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master - with: - oss-fuzz-project-name: 'libavif' - dry-run: false - - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master - with: - oss-fuzz-project-name: 'libavif' - fuzz-seconds: 600 - dry-run: false - - name: Upload Crash - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts - path: ./out/artifacts diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0c67dbdaac..732eeaeafa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,17 +52,6 @@ if(AVIF_ENABLE_FUZZTEST OR AVIF_ENABLE_GTEST OR AVIF_BUILD_APPS) target_link_libraries(aviftest_helpers_internal PRIVATE avif_enable_warnings) endif() -if(CMAKE_CXX_COMPILER_LOADED) - # Fuzz target without any fuzzing engine dependency. For easy reproduction of oss-fuzz issues. - add_executable(repro_avif_decode_fuzzer oss-fuzz/avif_decode_fuzzer.cc oss-fuzz/repro_fuzz.cc) - set_target_properties(repro_avif_decode_fuzzer PROPERTIES LINKER_LANGUAGE "CXX") - target_link_libraries(repro_avif_decode_fuzzer avif avif_enable_warnings) - # The test below exists for coverage and as a usage example: repro_avif_decode_fuzzer [reproducer file path] - add_test(NAME repro_avif_decode_fuzzer COMMAND repro_avif_decode_fuzzer - ${CMAKE_CURRENT_SOURCE_DIR}/data/color_grid_alpha_nogrid.avif - ) -endif() - ################################################################################ # GoogleTest diff --git a/tests/gtest/avif_fuzztest_dec.cc b/tests/gtest/avif_fuzztest_dec.cc index 2935c716c1..2aeeef516a 100644 --- a/tests/gtest/avif_fuzztest_dec.cc +++ b/tests/gtest/avif_fuzztest_dec.cc @@ -20,24 +20,36 @@ ::testing::Environment* const kStackLimitEnv = SetStackLimitTo512x1024Bytes(); //------------------------------------------------------------------------------ -void Decode(const std::string& arbitrary_bytes, DecoderPtr decoder) { +void Decode(const std::string& arbitrary_bytes, bool is_persistent, + DecoderPtr decoder) { ASSERT_FALSE(GetSeedDataDirs().empty()); // Make sure seeds are available. ImagePtr decoded(avifImageCreateEmpty()); ASSERT_NE(decoded, nullptr); - const avifResult result = avifDecoderReadMemory( - decoder.get(), decoded.get(), + + avifIO* const io = avifIOCreateMemoryReader( reinterpret_cast(arbitrary_bytes.data()), arbitrary_bytes.size()); - if (result == AVIF_RESULT_OK) { - EXPECT_GT(decoded->width, 0u); - EXPECT_GT(decoded->height, 0u); + if (io == nullptr) return; + // The Chrome's avifIO object is not persistent. + io->persistent = is_persistent; + avifDecoderSetIO(decoder.get(), io); + + if (avifDecoderParse(decoder.get()) != AVIF_RESULT_OK) return; + while (avifDecoderNextImage(decoder.get()) == AVIF_RESULT_OK) { + EXPECT_GT(decoder->image->width, 0u); + EXPECT_GT(decoder->image->height, 0u); + } + + // Loop once. + if (avifDecoderReset(decoder.get()) != AVIF_RESULT_OK) return; + while (avifDecoderNextImage(decoder.get()) == AVIF_RESULT_OK) { } } FUZZ_TEST(DecodeAvifTest, Decode) .WithDomains(ArbitraryImageWithSeeds({AVIF_APP_FILE_FORMAT_AVIF}), - ArbitraryAvifDecoder()); + Arbitrary(), ArbitraryAvifDecoder()); //------------------------------------------------------------------------------ diff --git a/tests/oss-fuzz/avif_decode_fuzzer.cc b/tests/oss-fuzz/avif_decode_fuzzer.cc deleted file mode 100644 index a619465caf..0000000000 --- a/tests/oss-fuzz/avif_decode_fuzzer.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 Joe Drago. All rights reserved. -// SPDX-License-Identifier: BSD-2-Clause - -#include "avif/avif.h" -#include "avif/avif_cxx.h" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { - avif::DecoderPtr decoder(avifDecoderCreate()); - if (decoder == nullptr) return 0; - decoder->allowProgressive = AVIF_TRUE; - - // ClusterFuzz passes -rss_limit_mb=2560 to avif_decode_fuzzer. Empirically - // setting decoder->imageSizeLimit to this value allows avif_decode_fuzzer to - // consume no more than 2560 MB of memory. Also limit the dimensions to avoid - // timeouts and to speed the fuzzing up. - // avifDecoderParse returns AVIF_RESULT_NOT_IMPLEMENTED if kImageSizeLimit is - // bigger than AVIF_DEFAULT_IMAGE_SIZE_LIMIT. - constexpr uint32_t kImageSizeLimit = 4 * 1024 * 4 * 1024; - static_assert(kImageSizeLimit <= AVIF_DEFAULT_IMAGE_SIZE_LIMIT, - "Too big an image size limit"); - decoder->imageSizeLimit = kImageSizeLimit; - - avifIO* const io = avifIOCreateMemoryReader(Data, Size); - if (io == nullptr) return 0; - // Simulate Chrome's avifIO object, which is not persistent. - io->persistent = AVIF_FALSE; - avifDecoderSetIO(decoder.get(), io); - - if (avifDecoderParse(decoder.get()) != AVIF_RESULT_OK) return 0; - while (avifDecoderNextImage(decoder.get()) == AVIF_RESULT_OK) { - } - - // Loop once. - if (avifDecoderReset(decoder.get()) != AVIF_RESULT_OK) return 0; - while (avifDecoderNextImage(decoder.get()) == AVIF_RESULT_OK) { - } - - return 0; // Non-zero return values are reserved for future use. -} diff --git a/tests/oss-fuzz/build.sh b/tests/oss-fuzz/build.sh index 9a61597f11..6327c205a5 100755 --- a/tests/oss-fuzz/build.sh +++ b/tests/oss-fuzz/build.sh @@ -83,12 +83,6 @@ cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DAVIF_CODEC_AOM=LOCAL -DAVIF_CODEC_DA ninja -# build decode fuzzer -$CXX $CXXFLAGS -std=c++11 -I../include \ - ../tests/oss-fuzz/avif_decode_fuzzer.cc -o $OUT/avif_decode_fuzzer \ - $LIB_FUZZING_ENGINE libavif.a ../ext/dav1d/build/src/libdav1d.a \ - ../ext/libyuv/build/libyuv.a ../ext/aom/build.libavif/libaom.a - # Restrict fuzztest tests to the only compatible fuzz engine: libfuzzer. if [[ "$FUZZING_ENGINE" == "libfuzzer" ]]; then # build fuzztests diff --git a/tests/oss-fuzz/repro_fuzz.cc b/tests/oss-fuzz/repro_fuzz.cc deleted file mode 100644 index f9735509ed..0000000000 --- a/tests/oss-fuzz/repro_fuzz.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2023 Google LLC -// SPDX-License-Identifier: BSD-2-Clause - -#include -#include -#include -#include -#include -#include - -using seconds = std::chrono::duration; -using chrono = std::chrono::high_resolution_clock; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); - -namespace { -std::vector ReadFile(const char* path) { - std::vector buffer; - std::ifstream file(path, std::ios::binary); - buffer.resize(file.seekg(0, std::ios::end).tellg()); - file.seekg(0, std::ios::beg) - .read(reinterpret_cast(buffer.data()), buffer.size()); - return buffer; -} -} // namespace - -int main(int argc, char* argv[]) { - if (argc < 2) { - std::cerr << "Missing reproducer file" << std::endl; - return 1; - } - - for (int i = 1; i < argc; ++i) { - const std::vector buffer = ReadFile(argv[i]); - const chrono::time_point start_time = chrono::now(); - LLVMFuzzerTestOneInput(buffer.data(), buffer.size()); - std::cout << "Reproducing " << argv[i] << " took " - << seconds(chrono::now() - start_time).count() << " seconds." - << std::endl; - } - return 0; -}