diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec52e7..689e926 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ option(OJPH_ENABLE_TIFF_SUPPORT "Enables input and output support for TIFF files option(OJPH_BUILD_TESTS "Enables building test code" OFF) option(OJPH_BUILD_EXECUTABLES "Enables building command line executables" ON) option(OJPH_BUILD_STREAM_EXPAND "Enables building ojph_stream_expand executable" OFF) +option(OJPH_BUILD_FUZZER "Enables building oss-fuzzing target executable" OFF) option(OJPH_DISABLE_SIMD "Disables the use of SIMD instructions -- agnostic to architectures" OFF) option(OJPH_DISABLE_SSE "Disables the use of SSE SIMD instructions and associated files" OFF) @@ -238,3 +239,15 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND OJPH_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() + +################################################################################################ +# Fuzzing +################################################################################################ + +if(OJPH_BUILD_FUZZER) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "Fuzzing requires a Clang toolchain.") + endif() + + add_subdirectory(fuzzing) +endif() \ No newline at end of file diff --git a/README.md b/README.md index 90064a7..19811ca 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The standard is available free of charge from [ITU website](https://www.itu.int/ * [Usage Example](./docs/usage_examples.md) * [Web-based Demos](./docs/web_demos.md) * [Doxygen Documentation Style](./docs/doxygen_style.md) +* [OSS-Fuzzing](./docs/fuzzing.md) # Repositories # [![Packaging status](https://repology.org/badge/vertical-allrepos/openjph.svg)](https://repology.org/project/openjph/versions) - diff --git a/docs/fuzzing.md b/docs/fuzzing.md new file mode 100644 index 0000000..ca599ce --- /dev/null +++ b/docs/fuzzing.md @@ -0,0 +1,13 @@ +# Fuzzer Target # + +Fuzzer targets can be build using the `OJPH_BUILD_FUZZER` build option. The Dockerfile in the `fuzzing directory` allows local testing: + +```sh +podman build -t openjph-fuzz -f fuzzing/Dockerfile +podman run -it --rm -v $(pwd):/app/ojph/ openjph-fuzz bash +image# mkdir /app/build/ +image# cd /app/build/ +image# cmake /app/ojph -DOJPH_BUILD_FUZZER=ON -DBUILD_SHARED_LIBS=OFF +image# make +image# ./fuzzing/ojph_expand_fuzz_target /app/jp2k_test_codestreams/openjph/*.j2c +``` diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt new file mode 100644 index 0000000..e099cf5 --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,10 @@ +if(DEFINED ENV{BUILD_UID}) + link_libraries($ENV{LIB_FUZZING_ENGINE}) +else() + add_compile_options(-fsanitize=fuzzer,address) + add_link_options(-fsanitize=fuzzer,address) +endif() + +add_executable(ojph_expand_fuzz_target fuzz_targets/ojph_expand_fuzz_target.cpp) +target_link_libraries(ojph_expand_fuzz_target PRIVATE openjph) + diff --git a/fuzzing/Dockerfile b/fuzzing/Dockerfile new file mode 100644 index 0000000..deb381b --- /dev/null +++ b/fuzzing/Dockerfile @@ -0,0 +1,8 @@ +FROM gcr.io/oss-fuzz-base/base-builder + +RUN apt-get update +RUN apt-get -y install cmake +RUN apt-get -y install libtiff-dev + +WORKDIR /app +RUN git clone --depth 1 https://github.com/aous72/jp2k_test_codestreams.git diff --git a/fuzzing/fuzz_targets/ojph_expand_fuzz_target.cpp b/fuzzing/fuzz_targets/ojph_expand_fuzz_target.cpp new file mode 100644 index 0000000..8c8be78 --- /dev/null +++ b/fuzzing/fuzz_targets/ojph_expand_fuzz_target.cpp @@ -0,0 +1,96 @@ +//***************************************************************************/ +// This software is released under the 2-Clause BSD license, included +// below. +// +// Copyright (c) 2019, Aous Naman +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//***************************************************************************/ +// This file is part of the OpenJPH software implementation. +// File: ojph_expand_fuzz_target.cpp +// Author: Pierre-Anthony Lemieux +// Date: 17 February 2026 +//***************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + try + { + + ojph::mem_infile infile; + infile.open(reinterpret_cast(Data), Size); + + ojph::codestream cs; + cs.read_headers(&infile); + + cs.create(); + + if (cs.is_planar()) + { + ojph::param_siz siz = cs.access_siz(); + for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) + { + ojph::ui32 height = siz.get_recon_height(c); + for (ojph::ui32 i = height; i > 0; --i) + { + ojph::ui32 comp_num; + cs.pull(comp_num); + assert(comp_num == c); + } + } + } + else + { + ojph::param_siz siz = cs.access_siz(); + ojph::ui32 height = siz.get_recon_height(0); + for (ojph::ui32 i = 0; i < height; ++i) + { + for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) + { + ojph::ui32 comp_num; + cs.pull(comp_num); + assert(comp_num == c); + } + } + } + } + catch (const std::exception &e) + { + std::cerr << e.what() << '\n'; + } + return 0; +}