diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 00000000..9598d047 --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,11 @@ +FROM gcr.io/oss-fuzz-base/base-builder-python +RUN apt-get update && \ + apt-get install -y make autoconf automake libtool python3.9 python3.9-dev && \ + ln -sf /usr/bin/python3.9 /usr/local/bin/python3 && \ + apt-get install -y python3-pip && \ + python3 -m pip install -v --no-cache-dir "atheris>=2.1.1" "pyinstaller==5.0.1" "coverage==6.3.2" +COPY . $SRC/live-py-plugin +WORKDIR live-py-plugin +RUN python3 -m pip install -e $SRC/live-py-plugin && \ + mv test/PySrc/tests/*_fuzzer.py .clusterfuzzlite/build.sh $SRC/ + diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100755 index 00000000..e59d44bc --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,27 @@ +pip3 install . + +# Build fuzzers into $OUT. These could be detected in other ways. +for fuzzer in $(find $SRC -name '*_fuzzer.py'); do + fuzzer_basename=$(basename -s .py $fuzzer) + fuzzer_package=${fuzzer_basename}.pkg + + # To avoid issues with Python version conflicts, or changes in environment + # over time on the OSS-Fuzz bots, we use pyinstaller to create a standalone + # package. Though not necessarily required for reproducing issues, this is + # required to keep fuzzers working properly in OSS-Fuzz. + pyinstaller --distpath $OUT --onefile --name $fuzzer_package $fuzzer + + # Create execution wrapper. Atheris requires that certain libraries are + # preloaded, so this is also done here to ensure compatibility and simplify + # test case reproduction. Since this helper script is what OSS-Fuzz will + # actually execute, it is also always required. + # NOTE: If you are fuzzing python-only code and do not have native C/C++ + # extensions, then remove the LD_PRELOAD line below as preloading sanitizer + # library is not required and can lead to unexpected startup crashes. + echo "#!/bin/sh +# LLVMFuzzerTestOneInput for fuzzer detection. +this_dir=\$(dirname \"\$0\") +ASAN_OPTIONS=\$ASAN_OPTIONS:symbolize=1:external_symbolizer_path=\$this_dir/llvm-symbolizer:detect_leaks=0 \ +\$this_dir/$fuzzer_package \$@" > $OUT/$fuzzer_basename + chmod +x $OUT/$fuzzer_basename +done diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 00000000..d1ad0ae5 --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: python diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..472cd146 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.tox/ +.git/ \ No newline at end of file diff --git a/.github/workflows/fuzz_pr.yml b/.github/workflows/fuzz_pr.yml new file mode 100644 index 00000000..3a8c7548 --- /dev/null +++ b/.github/workflows/fuzz_pr.yml @@ -0,0 +1,48 @@ +name: ClusterFuzzLite PR fuzzing +on: + pull_request: + paths: + - plugin/PySrc + - test/PySrc/tests/*_fuzzer.py +permissions: read-all +jobs: + PR: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: + - address + # Override this with the sanitizers you want. + # - undefined + # - memory + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: python + sanitizer: ${{ matrix.sanitizer }} + # Optional but recommended: used to only run fuzzers that are affected + # by the PR. + # See later section on "Git repo for storage". + # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git + # storage-repo-branch: main # Optional. Defaults to "main" + # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + fuzz-seconds: 600 + mode: 'code-change' + sanitizer: ${{ matrix.sanitizer }} + output-sarif: true + # Optional but recommended: used to download the corpus produced by + # batch fuzzing. + # See later section on "Git repo for storage". + # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git + # storage-repo-branch: main # Optional. Defaults to "main" + # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages".