Skip to content

Commit

Permalink
Merge pull request #1804 from kevinbackhouse/FuzzMore
Browse files Browse the repository at this point in the history
Add fuzzing dictionary and test all printStructure() options
  • Loading branch information
kevinbackhouse authored Jul 28, 2021
2 parents 0f0885b + db1529a commit 2488fc8
Show file tree
Hide file tree
Showing 11 changed files with 574 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/on_PR_linux_fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ jobs:
run: |
cd build
mkdir corpus
./bin/fuzz-read-print-write corpus ../test/data/ -jobs=$(nproc) -workers=$(nproc) -max_total_time=120 -max_len=4096
./bin/fuzz-read-print-write corpus ../test/data/ -dict=../fuzz/exiv2.dict -jobs=$(nproc) -workers=$(nproc) -max_total_time=120 -max_len=4096
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1040,12 +1040,31 @@ $ cd <exiv2dir>/build
$ make python_tests 2>&1 | grep FAIL
```
[TOC](#TOC)
<div id="4-5">
### 4.5 Test Summary
| *Tests* | Unix Style Platforms _(bash)_ | Visual Studio _(cmd.exe)_ |
|:-- |:--- |:-- |
| | $ cd \<exiv2dir\>/build | \> cd \<exiv2dir\>/build |
| tests | $ make tests | \> cmake --build . --config Release --target tests |
| bash_tests | $ make bash_tests | \> cmake --build . --config Release --target bash_tests |
| python_tests | $ make python_tests | \> cmake --build . --config Release --target python_tests |
| unit_test | $ make unit_test | \> cmake --build . --config Release --target unit_test |
| version_test | $ make version_test | \> cmake --build . --config Release --target version_test |
The name **bash_tests** is historical. They are implemented in python.
[TOC](#TOC)
<div id="4-6">
### 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
Note that it only works with clang compiler as libFuzzer is integrated with clang > 6.0
To build the fuzzers:
Expand All @@ -1064,21 +1083,7 @@ mkdir corpus
./bin/fuzz-read-print-write corpus ../test/data/ -jobs=$(nproc) -workers=$(nproc) -max_len=4096
```
[TOC](#TOC)
<div id="4-5">
### 4.5 Test Summary
| *Tests* | Unix Style Platforms _(bash)_ | Visual Studio _(cmd.exe)_ |
|:-- |:--- |:-- |
| | $ cd \<exiv2dir\>/build | \> cd \<exiv2dir\>/build |
| tests | $ make tests | \> cmake --build . --config Release --target tests |
| bash_tests | $ make bash_tests | \> cmake --build . --config Release --target bash_tests |
| python_tests | $ make python_tests | \> cmake --build . --config Release --target python_tests |
| unit_test | $ make unit_test | \> cmake --build . --config Release --target unit_test |
| version_test | $ make version_test | \> cmake --build . --config Release --target version_test |
The name **bash_tests** is historical. They are implemented in python.
For more information about fuzzing see [`fuzz/README.md`](fuzz/README.md).
[TOC](#TOC)
<div id="5">
Expand Down
63 changes: 63 additions & 0 deletions fuzz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Exiv2 fuzzing

This directory contains a [libFuzzer](https://llvm.org/docs/LibFuzzer.html) fuzzing target for Exiv2. The fuzzer is run for a short period of time on every pull request by the [`on_PR_linux_fuzz`](/.github/workflows/on_PR_linux_fuzz.yml) Action.

## Running the fuzzer

To run the fuzzer locally, first build it:

```bash
cd <exiv2dir>
mkdir build-fuzz
cd build-fuzz
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)
```

This is the command to run the fuzzer for 2 minutes:

```bash
cd <exiv2dir>/build-fuzz
mkdir corpus
./bin/fuzz-read-print-write corpus ../test/data/ -dict=../fuzz/exiv2.dict -jobs=$(nproc) -workers=$(nproc) -max_total_time=120
```

Alternatively, a simple script is provided for running the fuzzer in a continuous loop:

```bash
../fuzz/fuzzloop.sh
```

## Generating a dictionary

Fuzzers perform better with a [dictionary](https://llvm.org/docs/LibFuzzer.html#dictionaries). For example, suppose the code contains a condition like [this](https://github.com/Exiv2/exiv2/blob/15098f4ef50cc721ad0018218acab2ff06e60beb/src/xmpsidecar.cpp#L177-L179):

```c
if (xmpPacket_.substr(0, 5) != "<?xml") {
xmpPacket_ = xmlHeader + xmpPacket_ + xmlFooter;
}
```

Adding the string `"<?xml"` to the dictionary will help the fuzzer to trigger both branches of this condition.

This directory contains a simple [CodeQL query](mkdictionary.ql) which searches the source code for string literals that are used in conditions. Since the resulting dictionary is relatively small, and unlikely to need to change very often, it has been checked into the repository as a text file: [exiv2.dict](exiv2.dict).

To run the CodeQL query to generate a new dictionary, you first need to build a database:

```bash
cd <exiv2dir>
codeql database create --language=cpp exiv2db
```

Then run the query and convert the results to JSON:

```bash
codeql query run --database=exiv2db --output=dict.bqrs fuzz/mkdictionary.ql
codeql bqrs decode --format=json --output dict.json dict.bqrs
```

Finally, use [`mkdictionary.py`](mkdictionary.py) to convert the JSON to libFuzzer's dictionary format:

```bash
./fuzz/mkdictionary.py dict.json | sort > ./fuzz/exiv2.dict
```
Loading

0 comments on commit 2488fc8

Please sign in to comment.