commit 51d5c9b214674b244f90baa88b45c537c05cd2ee
parent 4eff43f8d167afbd4e25ca0d0cc78bc143f17096
Author: MTRNord <mtrnord1@gmail.com>
Date: Tue, 6 Aug 2024 20:01:16 +0200
Initial fuzzing prep
Diffstat:
13 files changed, 295 insertions(+), 10 deletions(-)
diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile
@@ -0,0 +1,15 @@
+FROM gcr.io/oss-fuzz-base/base-builder:v1
+RUN apt-get update && apt-get install -y \
+ automake \
+ autopoint \
+ cmake \
+ git \
+ python3-pip \
+ pkg-config
+RUN pip3 install meson ninja
+
+RUN git clone --depth 1 https://github.com/randombit/botan.git
+COPY . $SRC/spank-olm
+
+WORKDIR $SRC/spank-olm
+COPY ./.clusterfuzzlite/build.sh $SRC/
+\ No newline at end of file
diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh
@@ -0,0 +1,52 @@
+#!/bin/bash -eu
+
+export PKG_CONFIG="pkg-config --static"
+export PKG_CONFIG_PATH="$WORK/lib/pkgconfig"
+export CPPFLAGS="-I$WORK/include"
+export LDFLAGS="-L$WORK/lib"
+
+# Run as many parallel jobs as there are available CPU cores
+export MAKEFLAGS="-j$(nproc)"
+
+# botan
+pushd ../botan
+./configure.py --prefix="${WORK}"
+make
+make install
+cp build/botan-3.pc "${PKG_CONFIG_PATH}"
+popd
+
+# spank-olm
+meson setup build --prefix="${WORK}" --libdir=lib --prefer-static --default-library=static --buildtype=debugoptimized \
+ -Dfuzzing_engine=oss-fuzz -Dfuzzer_ldflags="$LIB_FUZZING_ENGINE" \
+ -Dcpp_link_args="$LDFLAGS -Wl,-rpath=\$ORIGIN"
+meson install -C build --tag devel
+
+# Copy fuzz executables to $OUT
+find build/fuzz -maxdepth 1 -executable -type f -exec cp -v '{}' "${OUT}" \;
+
+# All shared libraries needed during fuzz target execution should be inside the $OUT/lib directory
+mkdir -p "${OUT}"
+cp ${WORK}/lib/*.so "${OUT}/lib"
+
+# TODO: This is from libvips and needs adjusting:
+
+## Merge the seed corpus in a single directory, exclude files larger than 4k
+#mkdir -p fuzz/corpus
+#find \
+# $SRC/afl-testcases/{gif*,jpeg*,png,tiff,webp}/full/images \
+# fuzz/*_fuzzer_corpus \
+# test/test-suite/images \
+# -type f -size -4k \
+# -exec bash -c 'hash=($(sha1sum {})); mv {} fuzz/corpus/$hash' \;
+#zip -jrq $OUT/seed_corpus.zip fuzz/corpus
+#
+## Link corpus
+#for fuzzer in fuzz/*_fuzzer.cc; do
+# target=$(basename "$fuzzer" .cc)
+# ln -sf "seed_corpus.zip" "$OUT/${target}_seed_corpus.zip"
+#done
+#
+## Copy options and dictionary files to $OUT
+#find fuzz -name '*_fuzzer.dict' -exec cp -v '{}' $OUT \;
+#find fuzz -name '*_fuzzer.options' -exec cp -v '{}' $OUT \;
+\ No newline at end of file
diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml
@@ -0,0 +1,7 @@
+homepage: "https://github.com/MTRNord/spank-olm"
+language: c++
+primary_contact: "git@nordgedanken.dev"
+main_repo: 'https://github.com/MTRNord/spank-olm'
+
+fuzzing_engines:
+ - libfuzzer
+\ No newline at end of file
diff --git a/.github/workflows/cflite_batch.yml b/.github/workflows/cflite_batch.yml
@@ -0,0 +1,58 @@
+name: ClusterFuzzLite batch fuzzing
+on:
+ schedule:
+ - cron: '0 0 * * *' # Every 6th hour. Change this to whatever is suitable.
+permissions: read-all
+jobs:
+ BatchFuzzing:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ sanitizer:
+ - address
+ - undefined
+ - memory
+ steps:
+ - name: Build Fuzzers (${{ matrix.sanitizer }})
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@v1
+ with:
+ language: c++
+ sanitizer: ${{ matrix.sanitizer }}
+ - name: Run Fuzzers (${{ matrix.sanitizer }})
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@v1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 3600
+ mode: 'batch'
+ sanitizer: ${{ matrix.sanitizer }}
+ output-sarif: true
+ # Optional but recommended: For storing certain artifacts from 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".
+ Coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@v1
+ with:
+ language: c++
+ sanitizer: coverage
+ - name: Run Fuzzers
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@v1
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 600
+ mode: 'coverage'
+ sanitizer: 'coverage'
+ # Optional but recommended.
+ # 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".
diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml
@@ -0,0 +1,48 @@
+name: ClusterFuzzLite PR fuzzing
+on:
+ pull_request:
+ paths:
+ - '**'
+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
+ - undefined
+ - memory
+ steps:
+ - name: Build Fuzzers (${{ matrix.sanitizer }})
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@v1
+ with:
+ language: c++
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ 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:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ 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".
diff --git a/fuzz/meson.build b/fuzz/meson.build
@@ -0,0 +1,44 @@
+fuzz_deps = spank_olm_deps
+fuzz_ldflags = []
+
+if get_option('fuzzer_ldflags') != ''
+ fuzz_ldflags += [get_option('fuzzer_ldflags')]
+endif
+
+if fuzzing_engine == 'libfuzzer'
+ fuzz_ldflags += ['-fsanitize=fuzzer']
+endif
+
+fuzz_progs = [
+ 'olm_decryption_fuzzer'
+]
+
+fuzz_execs = []
+foreach fuzz_prog : fuzz_progs
+ fuzz_execs += executable(fuzz_prog,
+ fuzz_prog + '.cpp',
+ dependencies: [spank_olm_dep, fuzz_deps],
+ link_args: fuzz_ldflags
+ )
+endforeach
+
+# If the fuzzing engine is not OSS-Fuzz, build the unit tests to be run on CI
+if fuzzing_engine != 'oss-fuzz'
+ test_fuzz = configure_file(
+ input: 'test_fuzz.sh',
+ output: 'test_fuzz.sh',
+ copy: true,
+ )
+
+ test(
+ 'fuzz',
+ test_fuzz,
+ workdir: meson.current_build_dir(),
+ depends: [
+ fuzz_execs,
+ ],
+ # Increase the timeout as running the tests with sanitizers
+ # enabled could be slower than the default 30 seconds.
+ timeout: 60,
+ )
+endif
+\ No newline at end of file
diff --git a/fuzz/olm_decryption_fuzzer.cpp b/fuzz/olm_decryption_fuzzer.cpp
@@ -0,0 +1,5 @@
+#include <cstdint>
+#include <cstddef>
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return 0;
+}
+\ No newline at end of file
diff --git a/fuzz/test_fuzz.sh b/fuzz/test_fuzz.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+set -e
+
+ret=0
+
+for fuzzer in *_fuzzer; do
+ exit_code=0
+ "./${fuzzer}" || exit_code=$?
+ if [ $exit_code -ne 0 ]; then
+ echo FAIL "${fuzzer}"
+ ret=1
+ fi
+done
+
+exit $ret
+\ No newline at end of file
diff --git a/include/slap-olm.hpp b/include/slap-olm.hpp
@@ -0,0 +1 @@
+#pragma once
+\ No newline at end of file
diff --git a/main.cpp b/main.cpp
@@ -1,6 +0,0 @@
-#include <iostream>
-
-int main() {
- std::cout << "Hello, World!" << std::endl;
- return 0;
-}
diff --git a/meson.build b/meson.build
@@ -3,10 +3,34 @@ project('spank-olm', 'cpp',
default_options : ['warning_level=3',
'cpp_std=c++23',
'b_lto=true',
- 'b_thinlto_cache=true'])
+ 'b_thinlto_cache=true',
+ 'warning_level=3',])
-spank_olm = executable('spank_olm', 'main.cpp', install : true)
+# libFuzzer related things
+fuzzing_engine = get_option('fuzzing_engine')
+if fuzzing_engine == 'libfuzzer'
+ if not cc.has_argument('-fsanitize=fuzzer')
+ error('fuzzing_engine libfuzzer requires "-fsanitize=fuzzer"')
+ endif
+ fuzzer_args = ['-fsanitize=fuzzer-no-link', '-fsanitize=fuzzer']
+ add_project_arguments(cc.first_supported_argument(fuzzer_args), language : ['cpp', 'c'])
+endif
+
+# Cmake doesnt work with meson, so we need to require pkg-config
+botan_dep = dependency('botan-3', version : '>=3.6.0', required : true, method : 'pkg-config')
+
+spank_olm_deps = [botan_dep]
+
+incdir = include_directories('include')
+spank_olm = library('spank_olm', 'src/slap-olm.cpp', install : true, dependencies : spank_olm_deps, include_directories : incdir)
+
+spank_olm_dep = declare_dependency(
+ link_with: spank_olm,
+ dependencies: spank_olm_deps,
+)
snitch_dep = dependency('snitch')
-test('test', executable('spank-olm-test','tests/test.cpp',dependencies:snitch_dep))
-\ No newline at end of file
+test('test', executable('spank-olm-test','tests/test.cpp',dependencies:snitch_dep, link_with : spank_olm))
+
+subdir('fuzz')
+\ No newline at end of file
diff --git a/meson_options.txt b/meson_options.txt
@@ -0,0 +1,11 @@
+# fuzzing options
+
+option('fuzzing_engine',
+ type: 'combo',
+ choices : ['none', 'libfuzzer', 'oss-fuzz'],
+ value: 'none',
+ description: 'Select the fuzzing engine')
+
+option('fuzzer_ldflags',
+ type: 'string',
+ description: 'Extra LDFLAGS used during linking of fuzzing binaries')
+\ No newline at end of file
diff --git a/src/slap-olm.cpp b/src/slap-olm.cpp
@@ -0,0 +1 @@
+#include "slap-olm.hpp"
+\ No newline at end of file