Skip to content

Commit 57f349e

Browse files
committed
added fuzzing client
1 parent 00a131e commit 57f349e

File tree

3 files changed

+149
-4
lines changed

3 files changed

+149
-4
lines changed

.github/workflows/fuzz.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions
2+
# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners
3+
name: fuzz
4+
on: [pull_request]
5+
6+
permissions:
7+
contents: read
8+
9+
jobs:
10+
fuzz:
11+
runs-on: ubuntu-24.04
12+
if: ${{ github.repository_owner == 'danmar' }}
13+
14+
steps:
15+
- uses: actions/checkout@v6
16+
with:
17+
persist-credentials: false
18+
19+
# the man-db trigger causes package installations to stall for several minutes at times. so just drop the package.
20+
# see https://github.com/actions/runner/issues/4030
21+
- name: Remove man-db package
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get remove man-db
25+
26+
- name: Install missing software
27+
run: |
28+
sudo apt-get update
29+
sudo apt-get install -y make
30+
31+
- name: Install clang
32+
run: |
33+
wget https://apt.llvm.org/llvm.sh
34+
chmod +x llvm.sh
35+
sudo ./llvm.sh 21
36+
37+
- name: Build fuzzer
38+
id: build
39+
run: |
40+
# TODO: test O/LTO for best speed
41+
# TODO: use -stdlib=libc++ -lc++
42+
make -j$(nproc) CXX=clang++ CXXOPTS="-O3 -flto -fno-omit-frame-pointer -g -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize-address-use-after-scope -fno-sanitize=integer -fno-sanitize-recover=undefined" LDOPTS="-flto" LIB_FUZZING_ENGINE="-fsanitize=fuzzer" fuzz
43+
env:
44+
CXX: clang-21
45+
46+
- name: Run fuzzer
47+
run: |
48+
mkdir corpus
49+
mkdir artifacts
50+
./fuzz -only_ascii=1 -timeout=5 -fork=$(nproc) -use_value_profile=0 -max_total_time=60 -artifact_prefix=./artifacts/ corpus corpus_test
51+
52+
- name: Upload corpus
53+
uses: actions/upload-artifact@v6
54+
if: (success() || failure()) && steps.build.outcome == 'success'
55+
with:
56+
name: corpus
57+
path: ./corpus
58+
59+
- name: Upload artifacts
60+
uses: actions/upload-artifact@v6
61+
if: failure() && steps.build.outcome == 'success'
62+
with:
63+
name: artifacts
64+
path: ./artifacts

Makefile

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,32 @@ test.o: CPPFLAGS += $(TEST_CPPFLAGS)
1212
test.o: CXXFLAGS += -Wno-multichar
1313

1414
%.o: %.cpp simplecpp.h
15-
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $<
15+
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< $(LIB_FUZZING_ENGINE)
16+
17+
fuzz_no.o: fuzz.cpp
18+
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DNO_FUZZ -c -o $@ $^
1619

1720
testrunner: test.o simplecpp.o
18-
$(CXX) $(LDFLAGS) simplecpp.o test.o -o testrunner
21+
$(CXX) $(LDFLAGS) -o $@ $^
1922

2023
test: testrunner simplecpp
2124
./testrunner
2225
python3 run-tests.py
2326
python3 -m pytest integration_test.py -vv
2427

28+
fuzz: fuzz.o simplecpp.o
29+
# TODO: use -stdlib=libc++ -lc++
30+
# make fuzz CXX=clang++ CXXOPTS="-O2 -fno-omit-frame-pointer -g -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address,undefined -fsanitize-address-use-after-scope -fno-sanitize=integer -fno-sanitize-recover=undefined" LIB_FUZZING_ENGINE="-fsanitize=fuzzer"
31+
$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ $^ $(LIB_FUZZING_ENGINE)
32+
33+
no-fuzz: fuzz_no.o simplecpp.o
34+
$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ $^
35+
2536
selfcheck: simplecpp
2637
CXX=$(CXX) ./selfcheck.sh
2738

2839
simplecpp: main.o simplecpp.o
29-
$(CXX) $(LDFLAGS) main.o simplecpp.o -o simplecpp
40+
$(CXX) $(LDFLAGS) -o $@ $^
3041

3142
clean:
32-
rm -f testrunner simplecpp *.o
43+
rm -f testrunner fuzz no-fuzz simplecpp *.o

fuzz.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* simplecpp - A simple and high-fidelity C/C++ preprocessor library
3+
* Copyright (C) 2016-2024 simplecpp team
4+
*/
5+
6+
#include "simplecpp.h"
7+
8+
#include <cstdint>
9+
10+
#ifdef NO_FUZZ
11+
#include <cstdlib>
12+
#include <fstream>
13+
#include <sstream>
14+
#include <string>
15+
#endif
16+
17+
/*
18+
0 - store in corpus
19+
-1 - omit from corpus
20+
*/
21+
static int doProcess(const uint8_t *data, size_t dataSize)
22+
{
23+
simplecpp::OutputList outputList;
24+
std::vector<std::string> files;
25+
simplecpp::TokenList rawtokens(data, dataSize, files, "test.cpp", &outputList);
26+
27+
simplecpp::TokenList outputTokens(files);
28+
simplecpp::FileDataCache filedata;
29+
const simplecpp::DUI dui;
30+
std::list<simplecpp::MacroUsage> macroUsage;
31+
std::list<simplecpp::IfCond> ifCond;
32+
simplecpp::preprocess(outputTokens, rawtokens, files, filedata, dui, &outputList, &macroUsage, &ifCond);
33+
34+
simplecpp::cleanup(filedata);
35+
36+
return 0;
37+
}
38+
39+
#ifndef NO_FUZZ
40+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize);
41+
42+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize)
43+
{
44+
return doProcess(data, dataSize);
45+
}
46+
#else
47+
int main(int argc, char * argv[])
48+
{
49+
if (argc < 2 || argc > 3)
50+
return EXIT_FAILURE;
51+
52+
std::ifstream f(argv[1]);
53+
if (!f.is_open())
54+
return EXIT_FAILURE;
55+
56+
std::ostringstream oss;
57+
oss << f.rdbuf();
58+
59+
if (!f.good())
60+
return EXIT_FAILURE;
61+
62+
const int cnt = (argc == 3) ? std::stoi(argv[2]) : 1;
63+
64+
const std::string code = oss.str();
65+
for (int i = 0; i < cnt; ++i)
66+
doProcess(reinterpret_cast<const uint8_t*>(code.data()), code.size());
67+
68+
return EXIT_SUCCESS;
69+
}
70+
#endif

0 commit comments

Comments
 (0)