diff --git a/lib/netstd/Makefile.am b/lib/netstd/Makefile.am index e60b74c06b..d153d349cb 100644 --- a/lib/netstd/Makefile.am +++ b/lib/netstd/Makefile.am @@ -22,29 +22,26 @@ SUBDIRS = . all-local: $(DOTNETCORE) build -c Release -check-local: +check-local: precompile-fuzzers $(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj $(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj $(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj $(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj $(DOTNETCORE) test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj -# Opt-in. Not wired into check-local because it requires the -# SharpFuzz.CommandLine global tool and libfuzzer-dotnet binary, -# which are dev-only dependencies. Run manually: `make build-fuzzers`. +# Compile-only build of the 12 fuzzer variants, without SharpFuzz +# IL rewriting. Wired into check-local so CI catches source changes +# that would break the fuzzer build — the actual fuzzing tooling +# (SharpFuzz.CommandLine global tool, libfuzzer-dotnet) is a dev-only +# dependency not present in CI. +precompile-fuzzers: + ./buildfuzzers.sh --no-instrument + +# Opt-in full build: compiles and instruments the 12 fuzzer variants +# for actual fuzzing. Requires the SharpFuzz.CommandLine global tool +# and libfuzzer-dotnet binary. Run manually: `make build-fuzzers`. build-fuzzers: - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Parse -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Parse -p:Engine=Libfuzzer - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Roundtrip -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Parse -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Parse -p:Engine=Libfuzzer - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Roundtrip -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Parse -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Parse -p:Engine=Libfuzzer - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Roundtrip -p:Engine=AFL - $(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer + ./buildfuzzers.sh clean-local: $(RM) -r Thrift/bin @@ -63,6 +60,7 @@ clean-local: $(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/obj $(RM) -r Tests/Thrift.FuzzTests/bin $(RM) -r Tests/Thrift.FuzzTests/obj + $(RM) -r Tests/Thrift.FuzzTests/gen-netstd distdir: $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -102,5 +100,7 @@ EXTRA_DIST = \ Thrift.sln \ build.cmd \ build.sh \ + buildfuzzers.sh \ + runfuzzer.sh \ runtests.cmd \ runtests.sh diff --git a/lib/netstd/README.md b/lib/netstd/README.md index 9025f0c054..760c5efc67 100644 --- a/lib/netstd/README.md +++ b/lib/netstd/README.md @@ -55,7 +55,14 @@ Because of the different environment requirements, migration from C# takes sligh # Fuzzing -We use [SharpFuzz](https://github.com/Metalnem/sharpfuzz) (and its libfuzzer variant) to fuzz the Thrift protocol parsers. This is **not** integrated with oss-fuzz, so all fuzzing must be run locally. **Supported platform: Linux only.** The fuzzers are opt-in and are **not** built by `make check`; run `make build-fuzzers` (or `./buildfuzzers.sh`) explicitly. +We use [SharpFuzz](https://github.com/Metalnem/sharpfuzz) (and its libfuzzer variant) to fuzz the Thrift protocol parsers. This is **not** integrated with oss-fuzz, so all fuzzing must be run locally. **Supported platform: Linux only.** + +`make check` compiles the 12 fuzzer variants without SharpFuzz IL +instrumentation, so changes that break the fuzzer build will fail CI. +Full, instrumented builds suitable for actually running a fuzzer remain +opt-in: run `make build-fuzzers` (or `./buildfuzzers.sh`), which +additionally requires the SharpFuzz.CommandLine global tool and the +`libfuzzer-dotnet` native driver as described below. ## Prerequisites diff --git a/lib/netstd/buildfuzzers.sh b/lib/netstd/buildfuzzers.sh index cc784d495f..4060c09a41 100755 --- a/lib/netstd/buildfuzzers.sh +++ b/lib/netstd/buildfuzzers.sh @@ -21,38 +21,59 @@ set -e +# Parse arguments. --no-instrument performs code generation and builds the +# 12 fuzzer assemblies but skips SharpFuzz IL rewriting. Intended for +# `make check`, where we want to catch source changes that break the +# fuzzer build without requiring the dev-only SharpFuzz.CommandLine +# global tool or the libfuzzer-dotnet native driver. +INSTRUMENT=1 +for arg in "$@"; do + case "$arg" in + --no-instrument) + INSTRUMENT=0 + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [--no-instrument]" + exit 1 + ;; + esac +done + # Ensure the SharpFuzz.CommandLine global tool (runtimeconfig-pinned to # net9.0 in package 2.2.0) can roll forward onto the net10 runtime used # by this repo. Remove once SharpFuzz 2.3.0 (upstream PR #72) ships with # an updated runtimeconfig. export DOTNET_ROLL_FORWARD=Major -# Check for SHARPFUZZ_DIR environment variable -if [ -z "$SHARPFUZZ_DIR" ]; then - echo "Error: SHARPFUZZ_DIR environment variable is not set." - echo "Please set SHARPFUZZ_DIR to the location of your SharpFuzz installation." - echo "See README for installation instructions." - exit 1 -fi +if [ "$INSTRUMENT" = "1" ]; then + # Check for SHARPFUZZ_DIR environment variable + if [ -z "$SHARPFUZZ_DIR" ]; then + echo "Error: SHARPFUZZ_DIR environment variable is not set." + echo "Please set SHARPFUZZ_DIR to the location of your SharpFuzz installation." + echo "See README for installation instructions." + exit 1 + fi -# Verify libfuzzer-dotnet exists -LIBFUZZER="$SHARPFUZZ_DIR/libfuzzer-dotnet" -if [ ! -f "$LIBFUZZER" ]; then - echo "Error: libfuzzer-dotnet not found at $LIBFUZZER" - echo "Please ensure SharpFuzz is properly installed in $SHARPFUZZ_DIR" - echo "See README for installation instructions." - exit 1 -fi + # Verify libfuzzer-dotnet exists + LIBFUZZER="$SHARPFUZZ_DIR/libfuzzer-dotnet" + if [ ! -f "$LIBFUZZER" ]; then + echo "Error: libfuzzer-dotnet not found at $LIBFUZZER" + echo "Please ensure SharpFuzz is properly installed in $SHARPFUZZ_DIR" + echo "See README for installation instructions." + exit 1 + fi -# Verify the sharpfuzz instrumentation CLI is on PATH before we spend -# time building 12 assemblies that would otherwise fail to be instrumented. -if ! command -v sharpfuzz >/dev/null 2>&1; then - echo "Error: 'sharpfuzz' CLI not found on PATH." - echo "Install it with:" - echo " dotnet tool install --global SharpFuzz.CommandLine" - echo " export PATH=\"\$PATH:\$HOME/.dotnet/tools\"" - echo "See README for full installation instructions." - exit 1 + # Verify the sharpfuzz instrumentation CLI is on PATH before we spend + # time building 12 assemblies that would otherwise fail to be instrumented. + if ! command -v sharpfuzz >/dev/null 2>&1; then + echo "Error: 'sharpfuzz' CLI not found on PATH." + echo "Install it with:" + echo " dotnet tool install --global SharpFuzz.CommandLine" + echo " export PATH=\"\$PATH:\$HOME/.dotnet/tools\"" + echo "See README for full installation instructions." + exit 1 + fi fi # Find the local Thrift compiler @@ -108,6 +129,11 @@ for protocol in Binary Compact Json; do done # Step 3: Instrument the assemblies +if [ "$INSTRUMENT" = "0" ]; then + echo "Build complete (instrumentation skipped)." + exit 0 +fi + echo "Instrumenting assemblies for fuzzing ..." # Exclusions for instrumentation @@ -143,4 +169,4 @@ while IFS= read -r -d '' dll; do fi done < <(find "$OUTPUT_DIR" -maxdepth 1 -type f -name "*.dll" -print0) -echo "Build and instrumentation complete." \ No newline at end of file +echo "Build and instrumentation complete." \ No newline at end of file