From 5a26ef9f8138ce1179b65b04fd1dc92ef9475dc5 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Fri, 8 Jul 2022 18:18:48 +0000 Subject: [PATCH 1/7] Add `GoodBad` C# example project, integration test --- .github/workflows/ci.yml | 44 ++++++++++++++++++- src/ci/test-libfuzzer-dotnet.sh | 45 +++++++++++++++++++ src/integration-tests/GoodBad/GoodBad.cs | 46 ++++++++++++++++++++ src/integration-tests/GoodBad/GoodBad.csproj | 10 +++++ 4 files changed, 143 insertions(+), 2 deletions(-) create mode 100755 src/ci/test-libfuzzer-dotnet.sh create mode 100644 src/integration-tests/GoodBad/GoodBad.cs create mode 100644 src/integration-tests/GoodBad/GoodBad.csproj diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de857124d0..50ce8e7401 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -371,7 +371,7 @@ jobs: - uses: actions/cache@v3 id: cache-radamsa-build-linux with: - # key on the shell script only: this script fixes the + # key on the shell script only: this script fixes the # version to a particular commit, so if it changes we need to rebuild key: radamsa-linux-${{ hashFiles('src/ci/radamsa-linux.sh') }} path: artifacts @@ -388,7 +388,7 @@ jobs: - uses: actions/cache@v3 id: cache-radamsa-build-windows with: - # key on the shell script only: this script fixes the + # key on the shell script only: this script fixes the # version to a particular commit, so if it changes we need to rebuild key: radamsa-windows-${{ hashFiles('src/ci/radamsa-windows.sh') }} path: artifacts @@ -524,6 +524,11 @@ jobs: (cd libfuzzer-aarch64-crosscompile; make) cp -r libfuzzer-aarch64-crosscompile/fuzz.exe libfuzzer-aarch64-crosscompile/inputs artifacts/linux-libfuzzer-aarch64-crosscompile + # C# target. + sudo apt-get install -y dotnet-sdk-6.0 + (cd GoodBad; dotnet publish -c Release -o out) + mv GoodBad/out artifacts/GoodBadDotnet + - uses: actions/upload-artifact@v2.2.2 with: name: integration-test-artifacts @@ -603,3 +608,38 @@ jobs: with: name: integration-test-artifacts path: src/integration-tests/artifacts + integration-tests-linux: + runs-on: ubuntu-18.04 + needs: + - build-integration-tests-linux + - dotnet-fuzzing-tools-linux + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2.0.8 + with: + name: build-artifacts + path: build-artifacts + - uses: actions/download-artifact@v2.0.8 + with: + name: integration-test-artifacts + path: integration-test-artifacts + - name: test + shell: bash + run: | + set -ex -o pipefail + + ls build-artifacts + ls integration-test-artifacts + + export GOODBAD_DOTNET="${GITHUB_WORKSPACE}/integration-test-artifacts/GoodBadDotnet" + + export LIBFUZZER_DOTNET="${GITHUB_WORKSPACE}/build-artifacts/third-party/dotnet-fuzzing-linux/libfuzzer-dotnet/libfuzzer-dotnet" + chmod +x $LIBFUZZER_DOTNET + + export LIBFUZZER_DOTNET_LOADER="${GITHUB_WORKSPACE}/build-artifacts/third-party/dotnet-fuzzing-linux/LibFuzzerDotnetLoader/LibFuzzerDotnetLoader" + chmod +x $LIBFUZZER_DOTNET_LOADER + + export SHARPFUZZ="${GITHUB_WORKSPACE}/build-artifacts/third-party/dotnet-fuzzing-linux/sharpfuzz/SharpFuzz.CommandLine" + chmod +x $SHARPFUZZ + + ./src/ci/test-libfuzzer-dotnet.sh diff --git a/src/ci/test-libfuzzer-dotnet.sh b/src/ci/test-libfuzzer-dotnet.sh new file mode 100755 index 0000000000..af6a073ddf --- /dev/null +++ b/src/ci/test-libfuzzer-dotnet.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -ex -o pipefail + +# Required envronment variables: +# - GOODBAD_DOTNET +# - LIBFUZZER_DOTNET +# - LIBFUZZER_DOTNET_LOADER +# - SHARPFUZZ + +export GOODBAD_DLL='GoodBad/GoodBad.dll' + +TMP=$(mktemp -d) +cd $TMP + +cp -r ${GOODBAD_DOTNET} GoodBad + +# Instrument DLL under test. +${SHARPFUZZ} GoodBad/GoodBad.dll + +# Create seed and crash inputs. +printf 'good' > good.txt +printf 'bad!' > bad.txt + +# Test individual env vars. +export LIBFUZZER_DOTNET_TARGET_ASSEMBLY="${GOODBAD_DLL}" +export LIBFUZZER_DOTNET_TARGET_CLASS='GoodBad.Fuzzer' +export LIBFUZZER_DOTNET_TARGET_METHOD='TestInput' + +${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} good.txt + +# Expect nonzero exit. +! ${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} bad.txt + +# # Test delimited env var. +export LIBFUZZER_DOTNET_TARGET="${LIBFUZZER_DOTNET_TARGET_ASSEMBLY}:${LIBFUZZER_DOTNET_TARGET_CLASS}:${LIBFUZZER_DOTNET_TARGET_METHOD}" +unset LIBFUZZER_DOTNET_TARGET_ASSEMBLY +unset LIBFUZZER_DOTNET_TARGET_CLASS +unset LIBFUZZER_DOTNET_TARGET_METHOD + +${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} good.txt + +# Expect nonzero exit. +! ${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} bad.txt + +rm -rf $TMP diff --git a/src/integration-tests/GoodBad/GoodBad.cs b/src/integration-tests/GoodBad/GoodBad.cs new file mode 100644 index 0000000000..f40160e56c --- /dev/null +++ b/src/integration-tests/GoodBad/GoodBad.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace GoodBad; + +public class BinaryParser +{ + int count = 0; + + public void ProcessInput(ReadOnlySpan data) { + if (data.Length < 4) { + return; + } + + if (data[0] == 'b') { count++; } + if (data[1] == 'a') { count++; } + if (data[2] == 'd') { count++; } + if (data[3] == '!') { count++; } + + // Simulate an out-of-bounds access while parsing. + if (count >= 4) { + var _ = data[0xdead]; + } + } +} + +public class Fuzzer { + /// Preferred test method. + public static void TestInput(ReadOnlySpan data) { + var parser = new BinaryParser(); + parser.ProcessInput(data); + } + + /// Backwards-compatible test method for legacy code that can't use `Span` types. + /// + /// Incurs an extra copy of `data` per fuzzing iteration. + public static void TestInputCompat(byte[] data) { + var parser = new BinaryParser(); + parser.ProcessInput(data); + } + + /// Invalid static method that has a fuzzing-incompatible signature. + public static void BadSignature(ReadOnlySpan data) { + return; + } +} diff --git a/src/integration-tests/GoodBad/GoodBad.csproj b/src/integration-tests/GoodBad/GoodBad.csproj new file mode 100644 index 0000000000..fd23d00997 --- /dev/null +++ b/src/integration-tests/GoodBad/GoodBad.csproj @@ -0,0 +1,10 @@ + + + + 10.0 + net6.0 + enable + enable + + + From fb8da4a3a97dae48b1df1cd9bae1b01459db7f09 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 10:53:50 -0700 Subject: [PATCH 2/7] Fix typo --- src/ci/test-libfuzzer-dotnet.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/test-libfuzzer-dotnet.sh b/src/ci/test-libfuzzer-dotnet.sh index af6a073ddf..a3977e46df 100755 --- a/src/ci/test-libfuzzer-dotnet.sh +++ b/src/ci/test-libfuzzer-dotnet.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -ex -o pipefail -# Required envronment variables: +# Required environment variables: # - GOODBAD_DOTNET # - LIBFUZZER_DOTNET # - LIBFUZZER_DOTNET_LOADER From 47984d9fecfa732e09d4bf22f3ae7259f38e9783 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 11:26:12 -0700 Subject: [PATCH 3/7] Document that absolute paths are required --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50ce8e7401..fdb9211aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -631,6 +631,7 @@ jobs: ls build-artifacts ls integration-test-artifacts + # Must be absolute paths. export GOODBAD_DOTNET="${GITHUB_WORKSPACE}/integration-test-artifacts/GoodBadDotnet" export LIBFUZZER_DOTNET="${GITHUB_WORKSPACE}/build-artifacts/third-party/dotnet-fuzzing-linux/libfuzzer-dotnet/libfuzzer-dotnet" From 67c943b17ac092e7abb4d307dc5290f698b615d4 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 11:26:41 -0700 Subject: [PATCH 4/7] Remove debug command --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdb9211aff..866c131306 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -628,7 +628,6 @@ jobs: run: | set -ex -o pipefail - ls build-artifacts ls integration-test-artifacts # Must be absolute paths. From 4dde026410d1beff38e7b9ac3ad1d5cf88ddf3a6 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 11:26:49 -0700 Subject: [PATCH 5/7] Remove debug command --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 866c131306..715e803422 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -628,7 +628,6 @@ jobs: run: | set -ex -o pipefail - ls integration-test-artifacts # Must be absolute paths. export GOODBAD_DOTNET="${GITHUB_WORKSPACE}/integration-test-artifacts/GoodBadDotnet" From df43bc0a6e9da96dacf74d7c3ba731d580599fdc Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 11:27:10 -0700 Subject: [PATCH 6/7] Remove extra newline --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 715e803422..bc5241e310 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -628,7 +628,6 @@ jobs: run: | set -ex -o pipefail - # Must be absolute paths. export GOODBAD_DOTNET="${GITHUB_WORKSPACE}/integration-test-artifacts/GoodBadDotnet" From a56f83143968059b411de3b2b42469b68a31b585 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Sun, 10 Jul 2022 11:28:43 -0700 Subject: [PATCH 7/7] Remove double comment --- src/ci/test-libfuzzer-dotnet.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/test-libfuzzer-dotnet.sh b/src/ci/test-libfuzzer-dotnet.sh index a3977e46df..cc1fa4c5a5 100755 --- a/src/ci/test-libfuzzer-dotnet.sh +++ b/src/ci/test-libfuzzer-dotnet.sh @@ -31,7 +31,7 @@ ${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} good.txt # Expect nonzero exit. ! ${LIBFUZZER_DOTNET} --target_path=${LIBFUZZER_DOTNET_LOADER} bad.txt -# # Test delimited env var. +# Test delimited env var. export LIBFUZZER_DOTNET_TARGET="${LIBFUZZER_DOTNET_TARGET_ASSEMBLY}:${LIBFUZZER_DOTNET_TARGET_CLASS}:${LIBFUZZER_DOTNET_TARGET_METHOD}" unset LIBFUZZER_DOTNET_TARGET_ASSEMBLY unset LIBFUZZER_DOTNET_TARGET_CLASS