Skip to content

Commit 5f0a7ee

Browse files
committed
Fix fuzz testing
Assisted-by: Cursor
1 parent 1a97398 commit 5f0a7ee

File tree

6 files changed

+217
-47
lines changed

6 files changed

+217
-47
lines changed

Makefile.am

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,20 +348,86 @@ distclean-local:
348348
-rm -rf $(top_builddir)/test-arena
349349
-rm -f lib-built
350350

351-
# Fuzzing targets - delegate to separate makefile
351+
if ENABLE_FUZZING
352+
# Fuzzing-specific instrumented binaries
353+
noinst_PROGRAMS = src/fuzz-filterdiff src/fuzz-interdiff src/fuzz-rediff
354+
355+
src_fuzz_filterdiff_SOURCES = $(src_filterdiff_SOURCES)
356+
src_fuzz_filterdiff_LDADD = $(src_filterdiff_LDADD)
357+
src_fuzz_filterdiff_CFLAGS = $(AM_CFLAGS) $(FUZZ_CFLAGS)
358+
src_fuzz_filterdiff_CPPFLAGS = $(AM_CPPFLAGS) $(FUZZ_CPPFLAGS)
359+
360+
src_fuzz_interdiff_SOURCES = $(src_interdiff_SOURCES)
361+
src_fuzz_interdiff_LDADD = $(src_interdiff_LDADD)
362+
src_fuzz_interdiff_CFLAGS = $(AM_CFLAGS) $(FUZZ_CFLAGS)
363+
src_fuzz_interdiff_CPPFLAGS = $(AM_CPPFLAGS) $(FUZZ_CPPFLAGS)
364+
365+
src_fuzz_rediff_SOURCES = $(src_rediff_SOURCES)
366+
src_fuzz_rediff_LDADD = $(src_rediff_LDADD)
367+
src_fuzz_rediff_CFLAGS = $(AM_CFLAGS) $(FUZZ_CFLAGS)
368+
src_fuzz_rediff_CPPFLAGS = $(AM_CPPFLAGS) $(FUZZ_CPPFLAGS)
369+
370+
# Override CC and LINK for fuzzing targets to use AFL++ for both compilation and linking
371+
$(src_fuzz_filterdiff_OBJECTS): CC = $(AFL_CC)
372+
$(src_fuzz_interdiff_OBJECTS): CC = $(AFL_CC)
373+
$(src_fuzz_rediff_OBJECTS): CC = $(AFL_CC)
374+
375+
# Use AFL++ compiler for linking to include AFL++ runtime
376+
src_fuzz_filterdiff_LINK = $(AFL_CC) $(AM_CFLAGS) $(CFLAGS) $(FUZZ_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
377+
src_fuzz_interdiff_LINK = $(AFL_CC) $(AM_CFLAGS) $(CFLAGS) $(FUZZ_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
378+
src_fuzz_rediff_LINK = $(AFL_CC) $(AM_CFLAGS) $(CFLAGS) $(FUZZ_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
379+
380+
# Create fuzzing symlinks (like regular tools)
381+
src/fuzz-combinediff$(EXEEXT): src/fuzz-interdiff$(EXEEXT)
382+
ln -sf $(notdir $<) $@
383+
384+
src/fuzz-flipdiff$(EXEEXT): src/fuzz-interdiff$(EXEEXT)
385+
ln -sf $(notdir $<) $@
386+
387+
src/fuzz-lsdiff$(EXEEXT): src/fuzz-filterdiff$(EXEEXT)
388+
ln -sf $(notdir $<) $@
389+
390+
src/fuzz-grepdiff$(EXEEXT): src/fuzz-filterdiff$(EXEEXT)
391+
ln -sf $(notdir $<) $@
392+
393+
src/fuzz-patchview$(EXEEXT): src/fuzz-filterdiff$(EXEEXT)
394+
ln -sf $(notdir $<) $@
395+
396+
CLEANFILES += src/fuzz-combinediff src/fuzz-flipdiff src/fuzz-lsdiff src/fuzz-grepdiff src/fuzz-patchview
397+
398+
# Fuzzing targets - use instrumented binaries when available
352399
fuzz-help fuzz-corpus fuzz-test fuzz-analyze fuzz-clean fuzz-check fuzz-ci fuzz-stats:
353400
@if [ -f Makefile.fuzz ]; then \
401+
$(MAKE) -f Makefile.fuzz FUZZ_BINDIR="$(top_builddir)/src" $@; \
402+
else \
403+
echo "Fuzzing not available - Makefile.fuzz not found"; \
404+
fi
405+
406+
fuzz-filterdiff fuzz-interdiff fuzz-rediff fuzz-grepdiff fuzz-lsdiff: src/fuzz-filterdiff src/fuzz-interdiff src/fuzz-rediff src/fuzz-lsdiff$(EXEEXT) src/fuzz-grepdiff$(EXEEXT)
407+
@if [ -f Makefile.fuzz ]; then \
408+
$(MAKE) -f Makefile.fuzz FUZZ_BINDIR="$(top_builddir)/src" $@; \
409+
else \
410+
echo "Fuzzing not available - Makefile.fuzz not found"; \
411+
fi
412+
413+
else
414+
# Fuzzing targets - delegate to separate makefile (non-instrumented fallback)
415+
fuzz-help fuzz-corpus fuzz-test fuzz-analyze fuzz-clean fuzz-check fuzz-ci fuzz-stats:
416+
@if [ -f Makefile.fuzz ]; then \
417+
echo "Warning: Using non-instrumented binaries - configure with --enable-fuzzing for better results"; \
354418
$(MAKE) -f Makefile.fuzz $@; \
355419
else \
356420
echo "Fuzzing not available - Makefile.fuzz not found"; \
357421
fi
358422

359423
fuzz-filterdiff fuzz-interdiff fuzz-rediff fuzz-grepdiff fuzz-lsdiff:
360424
@if [ -f Makefile.fuzz ]; then \
425+
echo "Warning: Using non-instrumented binaries - configure with --enable-fuzzing for better results"; \
361426
$(MAKE) -f Makefile.fuzz $@; \
362427
else \
363428
echo "Fuzzing not available - Makefile.fuzz not found"; \
364429
fi
430+
endif
365431

366432
EXTRA_DIST = $(man_MANS) \
367433
tests/common.sh tests/soak-test \

Makefile.fuzz

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ CORPUS_DIR = $(FUZZ_DIR)/corpus
55
RESULTS_DIR = $(FUZZ_DIR)/results
66
TOOLS = filterdiff interdiff rediff grepdiff lsdiff
77

8+
# Allow overriding binary directory (for instrumented builds)
9+
FUZZ_BINDIR ?= src
10+
811
.PHONY: fuzz-help fuzz-corpus fuzz-clean fuzz-test fuzz-all fuzz-analyze
912
.PHONY: $(addprefix fuzz-, $(TOOLS))
1013

@@ -29,6 +32,9 @@ fuzz-help:
2932
@echo " - AFL++ installed (american-fuzzy-lop package)"
3033
@echo " - Tools built (run 'make' first)"
3134
@echo ""
35+
@echo "For best results (instrumented binaries):"
36+
@echo " ./configure --enable-fuzzing && make"
37+
@echo ""
3238
@echo "Quick start: make fuzz-corpus && make fuzz-test"
3339

3440
# Generate or update the fuzzing corpus
@@ -45,10 +51,15 @@ fuzz-corpus: $(FUZZ_DIR)/generate_corpus.sh
4551
@echo "Corpus ready: $$(ls -1 $(CORPUS_DIR) | wc -l) files"
4652

4753
# Quick fuzzing test (60 seconds)
48-
fuzz-test: fuzz-corpus src/filterdiff
54+
fuzz-test: fuzz-corpus $(FUZZ_BINDIR)/filterdiff
4955
@echo "Running quick fuzz test (60 seconds)..."
5056
@echo "This will test basic fuzzing functionality"
51-
timeout 60s $(FUZZ_DIR)/run_fuzz.sh filterdiff || true
57+
@if [ -f "$(FUZZ_BINDIR)/fuzz-filterdiff" ]; then \
58+
echo "Using instrumented binary: $(FUZZ_BINDIR)/fuzz-filterdiff"; \
59+
else \
60+
echo "Warning: Using non-instrumented binary: $(FUZZ_BINDIR)/filterdiff"; \
61+
fi
62+
FUZZ_BINDIR="$(FUZZ_BINDIR)" timeout 60s $(FUZZ_DIR)/run_fuzz.sh filterdiff || true
5263
@echo ""
5364
@echo "Quick test completed. Check fuzz/results/filterdiff/ for results"
5465
@if [ -d "$(RESULTS_DIR)/filterdiff/crashes" ] && [ -n "$$(ls -A $(RESULTS_DIR)/filterdiff/crashes 2>/dev/null)" ]; then \
@@ -58,43 +69,43 @@ fuzz-test: fuzz-corpus src/filterdiff
5869
fi
5970

6071
# Extended fuzzing on all tools (runs in background)
61-
fuzz-all: fuzz-corpus $(addprefix src/, $(TOOLS))
72+
fuzz-all: fuzz-corpus
6273
@echo "Starting extended fuzzing on all tools..."
6374
@echo "This will run fuzzing sessions in the background"
6475
@echo "Monitor with: ps aux | grep afl-fuzz"
6576
@echo "Stop with: pkill afl-fuzz"
6677
@echo ""
6778
@for tool in $(TOOLS); do \
68-
if [ -f "src/$$tool" ]; then \
79+
if [ -f "$(FUZZ_BINDIR)/$$tool" ] || [ -f "$(FUZZ_BINDIR)/fuzz-$$tool" ]; then \
6980
echo "Starting $$tool fuzzing in background..."; \
70-
nohup $(FUZZ_DIR)/run_fuzz.sh $$tool > $(RESULTS_DIR)/$$tool.log 2>&1 & \
81+
FUZZ_BINDIR="$(FUZZ_BINDIR)" nohup $(FUZZ_DIR)/run_fuzz.sh $$tool > $(RESULTS_DIR)/$$tool.log 2>&1 & \
7182
sleep 2; \
7283
fi; \
7384
done
7485
@echo ""
7586
@echo "All fuzzing sessions started. Logs in $(RESULTS_DIR)/*.log"
7687
@echo "Run 'make fuzz-analyze' periodically to check for crashes"
7788

78-
# Individual tool fuzzing targets
79-
fuzz-filterdiff: fuzz-corpus src/filterdiff
89+
# Individual tool fuzzing targets - depend on instrumented binaries when available
90+
fuzz-filterdiff: fuzz-corpus
8091
@echo "Starting filterdiff fuzzing (interactive)..."
81-
$(FUZZ_DIR)/run_fuzz.sh filterdiff
92+
FUZZ_BINDIR="$(FUZZ_BINDIR)" $(FUZZ_DIR)/run_fuzz.sh filterdiff
8293

83-
fuzz-interdiff: fuzz-corpus src/interdiff
94+
fuzz-interdiff: fuzz-corpus
8495
@echo "Starting interdiff fuzzing (interactive)..."
85-
$(FUZZ_DIR)/run_fuzz.sh interdiff
96+
FUZZ_BINDIR="$(FUZZ_BINDIR)" $(FUZZ_DIR)/run_fuzz.sh interdiff
8697

87-
fuzz-rediff: fuzz-corpus src/rediff
98+
fuzz-rediff: fuzz-corpus
8899
@echo "Starting rediff fuzzing (interactive)..."
89-
$(FUZZ_DIR)/run_fuzz.sh rediff
100+
FUZZ_BINDIR="$(FUZZ_BINDIR)" $(FUZZ_DIR)/run_fuzz.sh rediff
90101

91-
fuzz-grepdiff: fuzz-corpus src/grepdiff
102+
fuzz-grepdiff: fuzz-corpus
92103
@echo "Starting grepdiff fuzzing (interactive)..."
93-
$(FUZZ_DIR)/run_fuzz.sh grepdiff
104+
FUZZ_BINDIR="$(FUZZ_BINDIR)" $(FUZZ_DIR)/run_fuzz.sh grepdiff
94105

95-
fuzz-lsdiff: fuzz-corpus src/lsdiff
106+
fuzz-lsdiff: fuzz-corpus
96107
@echo "Starting lsdiff fuzzing (interactive)..."
97-
$(FUZZ_DIR)/run_fuzz.sh lsdiff
108+
FUZZ_BINDIR="$(FUZZ_BINDIR)" $(FUZZ_DIR)/run_fuzz.sh lsdiff
98109

99110
# Analyze fuzzing results
100111
fuzz-analyze: $(FUZZ_DIR)/analyze_crashes.sh

configure.ac

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,54 @@ AS_IF([test "x$with_pcre2" != xno],
103103
AC_MSG_RESULT(no)
104104
)
105105

106+
dnl Check for fuzzing support
107+
AC_MSG_CHECKING([whether to enable fuzzing support])
108+
AC_ARG_ENABLE([fuzzing],
109+
[AS_HELP_STRING([--enable-fuzzing],
110+
[enable AFL++ fuzzing instrumentation @<:@default=no@:>@])],
111+
[], [enable_fuzzing=no])
112+
AC_MSG_RESULT($enable_fuzzing)
113+
114+
AM_CONDITIONAL([ENABLE_FUZZING], [test "x$enable_fuzzing" = "xyes"])
115+
116+
if test "x$enable_fuzzing" = "xyes"; then
117+
# Check for AFL++ compilers (prefer modern versions)
118+
AC_CHECK_PROGS([AFL_CC], [afl-clang-fast afl-gcc-fast afl-clang afl-gcc], [])
119+
AC_CHECK_PROGS([AFL_CXX], [afl-clang-fast++ afl-g++-fast afl-clang++ afl-g++], [])
120+
121+
if test "x$AFL_CC" = "x"; then
122+
AC_MSG_ERROR([AFL++ compiler not found. Install AFL++ or disable fuzzing with --disable-fuzzing])
123+
fi
124+
125+
# Test if AFL++ compiler works (version compatibility check)
126+
AC_MSG_CHECKING([if $AFL_CC works])
127+
echo 'int main(){return 0;}' > conftest.c
128+
if $AFL_CC conftest.c -o conftest >/dev/null 2>&1; then
129+
AC_MSG_RESULT([yes])
130+
rm -f conftest.c conftest
131+
else
132+
AC_MSG_RESULT([no - version incompatibility detected])
133+
AC_MSG_WARN([AFL++ compiler has version issues, falling back to regular compiler with fuzzing flags])
134+
AFL_CC="$CC"
135+
if test "x$AFL_CXX" != "x"; then
136+
AFL_CXX="$CXX"
137+
fi
138+
rm -f conftest.c conftest
139+
fi
140+
141+
# Store AFL++ compilers for use in Makefile, but don't override CC during configure
142+
AC_SUBST([AFL_CC])
143+
AC_SUBST([AFL_CXX])
144+
145+
# Add fuzzing-specific flags for the Makefile
146+
FUZZ_CFLAGS="-g -O1 -fno-omit-frame-pointer"
147+
FUZZ_CPPFLAGS="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
148+
AC_SUBST([FUZZ_CFLAGS])
149+
AC_SUBST([FUZZ_CPPFLAGS])
150+
151+
AC_MSG_NOTICE([Fuzzing enabled - instrumented binaries will be built with $AFL_CC])
152+
fi
153+
106154
dnl Check diff/patch programs
107155
AC_MSG_CHECKING(for diff program)
108156
DIFF=diff

fuzz/README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ The fuzz testing setup targets the main patch processing tools:
4040

4141
## Usage
4242

43+
### Building with Instrumentation (Recommended)
44+
45+
For effective fuzzing, build patchutils with AFL++ instrumentation:
46+
47+
```bash
48+
# Configure with fuzzing support
49+
./configure --enable-fuzzing
50+
51+
# Build instrumented binaries
52+
make
53+
54+
# Now fuzzing will use instrumented binaries automatically
55+
make fuzz-test
56+
```
57+
4358
### Via Makefile (Recommended)
4459

4560
```bash
@@ -49,7 +64,7 @@ make fuzz-help
4964
# Generate/update corpus (includes latest git diffs)
5065
make fuzz-corpus
5166

52-
# Quick 60-second test
67+
# Quick 60-second test (uses instrumented binaries if available)
5368
make fuzz-test
5469

5570
# Fuzz specific tools
@@ -91,7 +106,21 @@ make fuzz-ci
91106

92107
## Integration
93108

94-
The fuzzer integrates with the existing testing infrastructure:
109+
The fuzzer integrates with the autotools build system and existing testing infrastructure:
110+
111+
### Autotools Integration
112+
- `--enable-fuzzing` configure option enables AFL++ instrumentation
113+
- Creates separate `fuzz-*` instrumented binaries alongside regular tools
114+
- Automatic detection of instrumented vs. non-instrumented binaries
115+
- Graceful fallback to regular binaries when instrumentation not available
116+
117+
### Testing Integration
95118
- Uses existing test cases as seed corpus
96119
- Validates discovered issues against invariant tests
97120
- Generates regression tests for fixed bugs
121+
122+
### Build System Features
123+
- Conditional compilation based on `--enable-fuzzing`
124+
- Proper compiler detection for AFL++ (afl-clang-fast, afl-gcc)
125+
- Fuzzing-specific CFLAGS and preprocessor definitions
126+
- Integration with existing Makefile targets

fuzz/analyze_crashes.sh

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ SCRIPT_DIR="$(dirname "$0")"
77
RESULTS_DIR="$SCRIPT_DIR/results"
88
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
99

10+
# Allow overriding binary directory (for instrumented builds)
11+
FUZZ_BINDIR="${FUZZ_BINDIR:-$PROJECT_DIR/src}"
12+
1013
echo "Analyzing AFL++ fuzzing results..."
1114

1215
if [ ! -d "$RESULTS_DIR" ]; then
@@ -39,7 +42,12 @@ analyze_tool_crashes() {
3942

4043
# Try to reproduce the crash
4144
echo " Reproducing crash..."
42-
if timeout 10s "$PROJECT_DIR/src/$tool" < "$crash_file" >/dev/null 2>&1; then
45+
# Use instrumented binary if available
46+
local binary_path="$FUZZ_BINDIR/$tool"
47+
if [ -f "$FUZZ_BINDIR/fuzz-$tool" ]; then
48+
binary_path="$FUZZ_BINDIR/fuzz-$tool"
49+
fi
50+
if timeout 10s "$binary_path" < "$crash_file" >/dev/null 2>&1; then
4351
echo " ⚠ Crash not reproduced (may be timing-dependent)"
4452
else
4553
local exit_code=$?
@@ -49,7 +57,7 @@ analyze_tool_crashes() {
4957
if command -v gdb >/dev/null 2>&1; then
5058
echo " Getting stack trace..."
5159
timeout 30s gdb -batch -ex run -ex bt -ex quit \
52-
--args "$PROJECT_DIR/src/$tool" < "$crash_file" 2>/dev/null | \
60+
--args "$binary_path" < "$crash_file" 2>/dev/null | \
5361
grep -A 20 "Program received signal" | head -20 || true
5462
fi
5563
fi
@@ -78,7 +86,12 @@ analyze_tool_crashes() {
7886
echo " ⚠ Large input file - may cause memory exhaustion"
7987
else
8088
echo " Testing with timeout..."
81-
if timeout 5s "$PROJECT_DIR/src/$tool" < "$hang_file" >/dev/null 2>&1; then
89+
# Use instrumented binary if available
90+
local binary_path="$FUZZ_BINDIR/$tool"
91+
if [ -f "$FUZZ_BINDIR/fuzz-$tool" ]; then
92+
binary_path="$FUZZ_BINDIR/fuzz-$tool"
93+
fi
94+
if timeout 5s "$binary_path" < "$hang_file" >/dev/null 2>&1; then
8295
echo " ✓ Completed within timeout"
8396
else
8497
echo " ⚠ Still hangs or crashes"

0 commit comments

Comments
 (0)