Skip to content

Commit

Permalink
Merge pull request #1773 from kevinbackhouse/fuzz
Browse files Browse the repository at this point in the history
Add fuzz target
  • Loading branch information
kevinbackhouse authored Jul 15, 2021
2 parents fa52d7e + e157fd6 commit 0208b50
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 4 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/on_PR_linux_fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Builds and runs the fuzz target for a short amount of time. This is
# mainly to protect the fuzz target from bitrot, but hopefully will
# also help to quickly catch some bugs before the PR is merged.

name: Linux-Ubuntu Quick Fuzz on PRs

on:
pull_request:
workflow_dispatch:

jobs:
Linux:
name: 'Ubuntu 20.04 - clang/libFuzzer'
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: install dependencies
run: sudo ./ci/install_dependencies.sh
- name: build and compile
run: |
mkdir build && cd build
cmake -DEXIV2_ENABLE_PNG=ON -DEXIV2_ENABLE_WEBREADY=ON -DEXIV2_ENABLE_CURL=ON -DEXIV2_ENABLE_BMFF=ON -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON -DCMAKE_CXX_COMPILER=$(which clang++) -DEXIV2_BUILD_FUZZ_TESTS=ON -DEXIV2_TEAM_USE_SANITIZERS=ON ..
make -j $(nproc)
- name: Fuzz
run: |
cd build
mkdir corpus
./bin/fuzz-read-print-write corpus ../test/data/ -jobs=$(nproc) -workers=$(nproc) -max_total_time=120 -max_len=4096
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ option( EXIV2_ENABLE_BMFF "Build with BMFF support"
option( EXIV2_BUILD_SAMPLES "Build sample applications" ON )
option( EXIV2_BUILD_EXIV2_COMMAND "Build exiv2 command-line executable" ON )
option( EXIV2_BUILD_UNIT_TESTS "Build unit tests" OFF )
option( EXIV2_BUILD_FUZZ_TESTS "Build fuzz tests (libFuzzer)" OFF )
option( EXIV2_BUILD_DOC "Add 'doc' target to generate documentation" OFF )

# Only intended to be used by Exiv2 developers/contributors
Expand Down Expand Up @@ -91,6 +92,14 @@ if( EXIV2_BUILD_UNIT_TESTS )
add_subdirectory ( unitTests )
endif()

if( EXIV2_BUILD_FUZZ_TESTS )
if ((NOT COMPILER_IS_CLANG) OR (NOT EXIV2_TEAM_USE_SANITIZERS))
message(FATAL_ERROR "You need to build with Clang and sanitizers for the fuzzers to work. "
"Use Clang and -DEXIV2_TEAM_USE_SANITIZERS=ON")
endif()
add_subdirectory ( fuzz )
endif()

if( EXIV2_BUILD_SAMPLES )
add_subdirectory( samples )
get_directory_property(SAMPLES DIRECTORY samples DEFINITION APPLICATIONS)
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ The file ReadMe.txt in a build bundle describes how to install the library on th
3. [Unit tests](#4-3)
4. [Python tests](#4-4)
5. [Test Summary](#4-5)
6. [Fuzzing](#4-6)
5. [Platform Notes](#5)
1. [Linux](#5-1)
2. [macOS](#5-2)
Expand Down Expand Up @@ -1039,6 +1040,30 @@ $ cd <exiv2dir>/build
$ make python_tests 2>&1 | grep FAIL
```
### 4.6 Fuzzing
The code for the fuzzers is in `exiv2dir/fuzz`
To build the fuzzers, use the *cmake* option `-DEXIV2_BUILD_FUZZ_TESTS=ON` and `-DEXIV2_TEAM_USE_SANITIZERS=ON`.
Note that it only works with clang compiler as libFuzzer is integrate with clang > 6.0
To build the fuzzers:
```bash
$ cd <exiv2dir>
$ rm -rf build-fuzz ; mkdir build-fuzz ; cd build-fuzz
$ cmake .. -DCMAKE_CXX_COMPILER=$(which clang++) -DEXIV2_BUILD_FUZZ_TESTS=ON -DEXIV2_TEAM_USE_SANITIZERS=ON
$ cmake --build .
```
To execute a fuzzer:
```bash
cd <exiv2dir>/build-fuzz
mkdir corpus
./bin/fuzz-read-print-write corpus ../test/data/ -jobs=$(nproc) -workers=$(nproc) -max_len=4096
```
[TOC](#TOC)
<div id="4-5">
Expand Down
4 changes: 3 additions & 1 deletion cmake/compilerFlags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ if ( MINGW OR UNIX OR MSYS ) # MINGW, Linux, APPLE, CYGWIN
set(SANITIZER_FLAGS "-fno-omit-frame-pointer -fsanitize=address")
endif()
elseif( COMPILER_IS_CLANG )
if ( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9 )
if ( EXIV2_BUILD_FUZZ_TESTS )
set(SANITIZER_FLAGS "-fsanitize=fuzzer-no-link")
elseif ( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9 )
set(SANITIZER_FLAGS "-fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all")
elseif ( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.4 )
set(SANITIZER_FLAGS "-fno-omit-frame-pointer -fsanitize=address,undefined")
Expand Down
1 change: 1 addition & 0 deletions cmake/printSummary.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ endif()
OptionOutput( "Building exiv2 command: " EXIV2_BUILD_EXIV2_COMMAND )
OptionOutput( "Building samples: " EXIV2_BUILD_SAMPLES )
OptionOutput( "Building unit tests: " EXIV2_BUILD_UNIT_TESTS )
OptionOutput( "Building fuzz tests: " EXIV2_BUILD_FUZZ_TESTS )
OptionOutput( "Building doc: " EXIV2_BUILD_DOC )
OptionOutput( "Building with coverage flags: " BUILD_WITH_COVERAGE )
OptionOutput( "Using ccache: " BUILD_WITH_CCACHE )
14 changes: 14 additions & 0 deletions fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

macro(fuzzer name)
add_executable(${name} ${name}.cpp)
set_target_properties(${name}
PROPERTIES
COMPILE_FLAGS "-fsanitize=fuzzer"
LINK_FLAGS "-fsanitize=fuzzer")
target_link_libraries(${name}
PRIVATE
exiv2lib
)
endmacro()

fuzzer(fuzz-read-print-write)
35 changes: 35 additions & 0 deletions fuzz/fuzz-read-print-write.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <exiv2/exiv2.hpp>

#include <iostream>
#include <iomanip>
#include <cassert>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t * data, size_t size) {
// Invalid files generate a lot of warnings, so switch off logging.
Exiv2::LogMsg::setLevel(Exiv2::LogMsg::mute);

Exiv2::XmpParser::initialize();
::atexit(Exiv2::XmpParser::terminate);

try {
Exiv2::DataBuf data_copy(data, size);
Exiv2::Image::UniquePtr image =
Exiv2::ImageFactory::open(data_copy.pData_, size);
assert(image.get() != 0);

image->readMetadata();
image->exifData();

// Print to a std::ostringstream so that the fuzzer doesn't
// produce lots of garbage on stdout.
std::ostringstream buffer;
image->printStructure(buffer, Exiv2::kpsNone);

image->writeMetadata();

} catch(...) {
// Exiv2 throws an exception if the metadata is invalid.
}

return 0;
}
4 changes: 2 additions & 2 deletions src/iptc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,13 @@ namespace Exiv2 {
#endif
}
}
#ifndef SUPPRESS_WARNINGS
else {
#ifndef SUPPRESS_WARNINGS
EXV_WARNING << "IPTC dataset " << IptcKey(dataSet, record)
<< " has invalid size " << sizeData << "; skipped.\n";
#endif
return 7;
}
#endif
pRead += sizeData;
}

Expand Down
2 changes: 1 addition & 1 deletion src/jpgimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ namespace Exiv2 {
#endif
return -2;
}
#ifndef EXIV2_DEBUG_MESSAGES
#ifdef EXIV2_DEBUG_MESSAGES
if ( (dataSize & 1)
&& position + dataSize == static_cast<uint32_t>(sizePsData)) {
std::cerr << "Warning: "
Expand Down
2 changes: 2 additions & 0 deletions src/tiffvisitor_int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,9 @@ namespace Exiv2 {
tc->setStart(p);
object->addChild(std::move(tc));
} else {
#ifndef SUPPRESS_WARNINGS
EXV_WARNING << "Unable to handle tag " << tag << ".\n";
#endif
}
p += 12;
}
Expand Down

0 comments on commit 0208b50

Please sign in to comment.