commit
0208b508a0
30
.github/workflows/on_PR_linux_fuzz.yml
vendored
Normal file
30
.github/workflows/on_PR_linux_fuzz.yml
vendored
Normal file
@ -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
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
25
README.md
25
README.md
@ -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)
|
||||
@ -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">
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
fuzz/CMakeLists.txt
Normal file
14
fuzz/CMakeLists.txt
Normal file
@ -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
fuzz/fuzz-read-print-write.cpp
Normal file
35
fuzz/fuzz-read-print-write.cpp
Normal file
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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: "
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user