Skip to content

Commit 0a615f8

Browse files
fix: issue found by fuzzing (#846)
* Add the beginnings of a fuzzing system for CLI11. This commit adds the fuzzing code, a simple test, and two fixes to issues(seg faults) found by the initial round of fuzzing. It also adds a few tests and coverage issues uncovered in the process of developing the fuzz tests. As a side effect adjusts some of the azure tests to specify the vmImage which was being changed on azure. * update license to match rest of code base --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent fc9cea6 commit 0a615f8

23 files changed

+605
-27
lines changed

.codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ ignore:
44
- "book"
55
- "docs"
66
- "test_package"
7+
- "fuzz"

.github/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
codecov:
22
notify:
3-
after_n_builds: 4
3+
after_n_builds: 8
44
coverage:
55
status:
66
project:

.github/workflows/fuzz.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Fuzz
2+
on:
3+
workflow_dispatch:
4+
push:
5+
branches:
6+
- main
7+
- v*
8+
pull_request:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
quick_fuzz1:
16+
name: quickfuzz1
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v3
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Configure
24+
run: |
25+
cmake -S . -B build \
26+
-DCMAKE_CXX_STANDARD=17 \
27+
-DCLI11_SINGLE_FILE_TESTS=OFF \
28+
-DCLI11_BUILD_EXAMPLES=OFF \
29+
-DCLI11_FUZZ_TARGET=ON \
30+
-DCLI11_BUILD_TESTS=OFF \
31+
-DCLI11_BUILD_DOCS=OFF \
32+
-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_COMPILER_FORCED=ON \
33+
-DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=fuzzer,undefined,address"
34+
35+
- name: Build
36+
run: cmake --build build -j4
37+
38+
- name: Test
39+
run: |
40+
cd build
41+
make QUICK_CLI11_APP_FUZZ
42+
43+
- name: Test2
44+
run: |
45+
cd build
46+
make QUICK_CLI11_FILE_FUZZ
47+
48+
49+
- name: artifacts
50+
if: failure()
51+
uses: actions/upload-artifact@v3
52+
with:
53+
name: file_failure
54+
path: ./build/fuzz/cli11_*_fail_artifact.txt

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Prepare coverage
4949
run: |
5050
lcov --directory . --capture --output-file coverage.info
51-
lcov --remove coverage.info '*/tests/*' '*/examples/*' '/usr/*' '*/book/*' --output-file coverage.info
51+
lcov --remove coverage.info '*/tests/*' '*/examples/*' '/usr/*' '*/book/*' '*/fuzz/*' --output-file coverage.info
5252
lcov --list coverage.info
5353
working-directory: build
5454

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
exclude: ^(.github/workflows/|docs/img/)
12
ci:
23
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
34
autofix_commit_msg: "style: pre-commit.ci fixes"

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ endif()
132132

133133
include(CLI11Warnings)
134134

135+
# build the fuzzing example or fuzz entry point
136+
add_subdirectory(fuzz)
137+
135138
add_subdirectory(src)
136139

137140
# Allow tests to be run on CUDA

azure-pipelines.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ jobs:
2828
- bash: cpplint --counting=detailed --recursive examples include/CLI tests
2929
displayName: Checking against google style guide
3030

31-
# TODO: Fix macOS error and windows warning in c++17 mode
3231
- job: Native
3332
strategy:
3433
matrix:
@@ -38,13 +37,13 @@ jobs:
3837
vmImage: "ubuntu-latest"
3938
cli11.precompile: ON
4039
macOS17:
41-
vmImage: "macOS-latest"
40+
vmImage: "macOS-12"
4241
cli11.std: 17
4342
macOS11:
44-
vmImage: "macOS-latest"
43+
vmImage: "macOS-11"
4544
cli11.std: 11
4645
macOS11PC:
47-
vmImage: "macOS-latest"
46+
vmImage: "macOS-11"
4847
cli11.std: 11
4948
cli11.precompile: ON
5049
Windows17:

fuzz/CMakeLists.txt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
2+
# under NSF AWARD 1414736 and by the respective contributors.
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: BSD-3-Clause
6+
7+
if(CMAKE_CXX_STANDARD GREATER 16)
8+
if(CLI11_FUZZ_TARGET)
9+
10+
add_executable(cli11_app_fuzzer cli11_app_fuzz.cpp fuzzApp.cpp fuzzApp.hpp)
11+
target_link_libraries(cli11_app_fuzzer PUBLIC CLI11)
12+
set_property(TARGET cli11_app_fuzzer PROPERTY FOLDER "Tests")
13+
14+
add_executable(cli11_file_fuzzer cli11_file_fuzz.cpp fuzzApp.cpp fuzzApp.hpp)
15+
target_link_libraries(cli11_file_fuzzer PUBLIC CLI11)
16+
set_property(TARGET cli11_file_fuzzer PROPERTY FOLDER "Tests")
17+
18+
if(NOT CLI11_FUZZ_ARTIFACT_PATH)
19+
set(CLI11_FUZZ_ARTIFACT_PATH ${PROJECT_BINARY_DIR}/fuzz)
20+
endif()
21+
22+
if(NOT CLI11_FUZZ_TIME)
23+
set(CLI11_FUZZ_TIME 360)
24+
endif()
25+
add_custom_target(
26+
QUICK_CLI11_APP_FUZZ
27+
COMMAND ${CMAKE_COMMAND} -E make_directory corp
28+
COMMAND
29+
cli11_app_fuzzer corp -max_total_time=${CLI11_FUZZ_TIME} -max_len=2048
30+
-dict=${CMAKE_CURRENT_SOURCE_DIR}/fuzz_dictionary1.txt
31+
-exact_artifact_path=${CLI11_FUZZ_ARTIFACT_PATH}/cli11_app_fail_artifact.txt)
32+
33+
add_custom_target(
34+
QUICK_CLI11_FILE_FUZZ
35+
COMMAND ${CMAKE_COMMAND} -E make_directory corp
36+
COMMAND
37+
cli11_file_fuzzer corp -max_total_time=${CLI11_FUZZ_TIME} -max_len=2048
38+
-dict=${CMAKE_CURRENT_SOURCE_DIR}/fuzz_dictionary2.txt
39+
-exact_artifact_path=${CLI11_FUZZ_ARTIFACT_PATH}/cli11_file_fail_artifact.txt)
40+
41+
else()
42+
if(CLI11_BUILD_EXAMPLES)
43+
add_executable(cli11Fuzz fuzzCommand.cpp fuzzApp.cpp fuzzApp.hpp)
44+
target_link_libraries(cli11Fuzz PUBLIC CLI11)
45+
set_property(TARGET cli11Fuzz PROPERTY FOLDER "Examples")
46+
endif()
47+
endif()
48+
endif()

fuzz/cli11_app_fuzz.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
2+
// under NSF AWARD 1414736 and by the respective contributors.
3+
// All rights reserved.
4+
//
5+
// SPDX-License-Identifier: BSD-3-Clause
6+
7+
#include "fuzzApp.hpp"
8+
#include <CLI/CLI.hpp>
9+
#include <cstring>
10+
#include <exception>
11+
#include <string>
12+
13+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
14+
if(Size == 0) {
15+
return 0;
16+
}
17+
std::string parseString(reinterpret_cast<const char *>(Data), Size);
18+
19+
CLI::FuzzApp fuzzdata;
20+
21+
auto app = fuzzdata.generateApp();
22+
try {
23+
app->parse(parseString);
24+
} catch(const CLI::ParseError &e) {
25+
//(app)->exit(e);
26+
// this just indicates we caught an error known by CLI
27+
}
28+
29+
return 0; // Non-zero return values are reserved for future use.
30+
}

fuzz/cli11_file_fuzz.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
2+
// under NSF AWARD 1414736 and by the respective contributors.
3+
// All rights reserved.
4+
//
5+
// SPDX-License-Identifier: BSD-3-Clause
6+
7+
#include "fuzzApp.hpp"
8+
#include <CLI/CLI.hpp>
9+
#include <cstring>
10+
#include <exception>
11+
#include <sstream>
12+
#include <string>
13+
14+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
15+
if(Size == 0) {
16+
return 0;
17+
}
18+
std::string parseString(reinterpret_cast<const char *>(Data), Size);
19+
std::stringstream out(parseString);
20+
CLI::FuzzApp fuzzdata;
21+
22+
auto app = fuzzdata.generateApp();
23+
try {
24+
app->parse_from_stream(out);
25+
} catch(const CLI::ParseError &e) {
26+
(app)->exit(e);
27+
// this just indicates we caught an error known by CLI
28+
}
29+
30+
return 0; // Non-zero return values are reserved for future use.
31+
}

0 commit comments

Comments
 (0)