Skip to content

Commit 2ef7541

Browse files
committed
Add local fuzzing infrastructure and fix library issues
1 parent f3bdfda commit 2ef7541

27 files changed

+2033
-158
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ __pycache__/
88
*.pyc
99
*.pyo
1010

11-
build/*
11+
build/*
12+
13+
fuzzing/fuzz_local/build/
14+
fuzzing/fuzz_local/corpora/
15+
fuzzing/fuzz_local/logs/
16+
fuzzing/fuzz_local/coverage/
17+

Makefile

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Simple Makefile for zxlib tests
22
# Wrapper around CMake build system
33

4-
.PHONY: all build test clean test-verbose test-filter format
4+
.PHONY: all build test clean test-verbose test-filter fuzz fuzz_crash fuzz_clean build_fuzz format
55

66
# Default target
77
all: build
@@ -29,4 +29,32 @@ test-filter: build
2929
cd build && ./zxlib_tests --gtest_filter="$(FILTER)"
3030

3131
format:
32-
find src evm tests include app \( -iname '*.h' -o -iname '*.c' -o -iname '*.cpp' -o -iname '*.hpp' \) | xargs clang-format -i
32+
find src evm tests include app fuzzing \( -iname '*.h' -o -iname '*.c' -o -iname '*.cpp' -o -iname '*.hpp' \) | xargs clang-format -i
33+
34+
# === FUZZING TARGETS ===
35+
# Proxy fuzzing targets to fuzzing/Makefile for convenience
36+
build_fuzz:
37+
@cd fuzzing && $(MAKE) build_fuzz
38+
39+
fuzz:
40+
@cd fuzzing && $(MAKE) fuzz
41+
42+
fuzz_crash:
43+
@cd fuzzing && $(MAKE) fuzz_crash
44+
45+
fuzz_clean:
46+
@cd fuzzing && $(MAKE) fuzz_clean
47+
48+
fuzz_report:
49+
@cd fuzzing && $(MAKE) fuzz_report
50+
51+
fuzz_report_html:
52+
@cd fuzzing && $(MAKE) fuzz_report_html
53+
54+
fuzz_help:
55+
@echo "Fuzzing targets for ledger-zxlib:"
56+
@echo ""
57+
@echo "All fuzzing commands have been moved to fuzzing/Makefile"
58+
@echo "You can either:"
59+
@echo " 1. Use proxy commands from here: make fuzz, make fuzz_crash, etc."
60+
@echo " 2. Go to fuzzing directory: cd fuzzing && make help"

fuzzing/FuzzingCommon.cmake

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ endif()
4848
if(ENABLE_COVERAGE)
4949
message(STATUS "Coverage enabled")
5050

51-
# Create coverage directory in project fuzz folder
52-
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage")
51+
# Create coverage directory in project folder
52+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/coverage")
5353

5454
# Add coverage instrumentation
55-
string(APPEND CMAKE_C_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
56-
string(APPEND CMAKE_CXX_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
57-
string(APPEND CMAKE_LINKER_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/fuzz/coverage/%p.profraw -fcoverage-mapping")
55+
string(APPEND CMAKE_C_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
56+
string(APPEND CMAKE_CXX_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
57+
string(APPEND CMAKE_LINKER_FLAGS " -fprofile-instr-generate=${CMAKE_CURRENT_SOURCE_DIR}/coverage/%p.profraw -fcoverage-mapping")
5858
endif()
5959

6060
# Sanitizers configuration

fuzzing/Makefile

Lines changed: 197 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,235 @@
1-
# Makefile for Python formatting in fuzzing infrastructure
2-
# This file provides standardized Python code formatting rules for Zondax Ledger applications
1+
# Makefile for ledger-zxlib fuzzing
2+
# This file contains both fuzzing targets and Python formatting rules
33

4-
# Configuration
4+
# === FUZZING CONFIGURATION ===
5+
# All fuzzing configuration is now managed in fuzz_local/fuzz_config.py
6+
FUZZ_DIR = fuzz_local
7+
FUZZ_BUILD_DIR = $(FUZZ_DIR)/build
8+
FUZZ_COVERAGE_DIR = $(FUZZ_DIR)/coverage
9+
PYTHON ?= python3
10+
11+
# === PYTHON FORMATTING CONFIGURATION ===
512
BLACK_LINE_LENGTH := 120
6-
# Only search in fuzzing-related directories (relative to this Makefile's location)
713
FUZZING_DIR := .
8-
PROJECT_FUZZ_DIR := $(shell cd ../../../ && pwd)/fuzz
9-
10-
# Validate that the project fuzz directory exists
11-
validate_fuzz_dir:
12-
@if [ ! -d "$(PROJECT_FUZZ_DIR)" ]; then \
13-
echo "⚠️ WARNING: Project fuzz directory not found: $(PROJECT_FUZZ_DIR)"; \
14-
echo " Only formatting files in the common fuzzing library directory."; \
15-
echo " To format project-specific files, please create the 'fuzz' directory."; \
16-
fi
17-
18-
# Find all Python files in fuzzing directories only
19-
PYTHON_FILES := $(shell find $(FUZZING_DIR) -name "*.py" -type f -not -path "*/.*" 2>/dev/null) \
20-
$(shell if [ -d "$(PROJECT_FUZZ_DIR)" ]; then find $(PROJECT_FUZZ_DIR) -name "*.py" -type f -not -path "*/.*" 2>/dev/null; fi)
2114

2215
# Default target
2316
.PHONY: help
2417
help:
25-
@echo "Python Formatting Rules for Zondax Ledger Fuzzing"
18+
@echo "ledger-zxlib Fuzzing & Development Tools"
2619
@echo ""
27-
@echo "Available targets:"
28-
@echo " format Format Python files in fuzzing directories with Black"
29-
@echo " check Check if files need formatting (no changes)"
30-
@echo " install Install Black formatter"
31-
@echo " clean Remove __pycache__ directories from fuzzing dirs"
32-
@echo " list List Python files in fuzzing directories"
20+
@echo "=== FUZZING TARGETS ==="
21+
@echo " build_fuzz Build fuzzer targets"
22+
@echo " fuzz Run all fuzzers (uses config from fuzz_config.py)"
23+
@echo " fuzz_crash Analyze any crashes found"
24+
@echo " fuzz_clean Clean fuzzing artifacts"
25+
@echo " fuzz_report Generate coverage report"
26+
@echo " fuzz_report_html Generate HTML coverage report"
27+
@echo ""
28+
@echo "=== PYTHON FORMATTING ==="
29+
@echo " format Format Python files with Black"
30+
@echo " check Check if files need formatting"
31+
@echo " format_clean Remove Python cache files"
3332
@echo ""
3433
@echo "Configuration:"
35-
@echo " Line length: $(BLACK_LINE_LENGTH)"
36-
@echo " Fuzzing directories: $(FUZZING_DIR) $(PROJECT_FUZZ_DIR)"
34+
@echo " Edit fuzz_local/fuzz_config.py to change fuzzing parameters"
35+
@echo ""
36+
@echo "Examples:"
37+
@echo " make fuzz # Run with default config"
38+
@echo " make fuzz_report # Generate coverage report"
39+
40+
# === FUZZING TARGETS ===
41+
42+
# Check if clang is available
43+
.PHONY: check_clang
44+
check_clang:
45+
@command -v clang >/dev/null 2>&1 || { \
46+
echo "❌ Error: clang not found. Please install clang for fuzzing."; \
47+
echo "On macOS: brew install llvm"; \
48+
echo "On Ubuntu: apt install clang"; \
49+
exit 1; \
50+
}
51+
@command -v clang++ >/dev/null 2>&1 || { \
52+
echo "❌ Error: clang++ not found. Please install clang++ for fuzzing."; \
53+
echo "On macOS: brew install llvm"; \
54+
echo "On Ubuntu: apt install clang"; \
55+
exit 1; \
56+
}
57+
@echo "✅ Clang found: $$(clang --version | head -n1)"
58+
@echo "✅ Clang++ found: $$(clang++ --version | head -n1)"
59+
60+
# Build fuzzer targets
61+
.PHONY: build_fuzz
62+
build_fuzz: check_clang
63+
@echo "🔨 Building fuzzer targets..."
64+
@mkdir -p $(FUZZ_BUILD_DIR)
65+
@cd $(FUZZ_BUILD_DIR) && \
66+
cmake -DCMAKE_C_COMPILER=clang \
67+
-DCMAKE_CXX_COMPILER=clang++ \
68+
-DENABLE_FUZZING=ON \
69+
-DENABLE_SANITIZERS=ON \
70+
-DCMAKE_BUILD_TYPE=Debug \
71+
.. && \
72+
make -j$$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
73+
@echo "✅ Fuzzer build completed"
74+
75+
# Run fuzzers
76+
.PHONY: fuzz
77+
fuzz: build_fuzz
78+
@echo "🚀 Starting fuzzing session..."
79+
@echo " Configuration from: $(FUZZ_DIR)/fuzz_config.py"
80+
@$(PYTHON) $(FUZZ_DIR)/run_local_fuzz.py
81+
@echo "🏁 Fuzzing session completed"
82+
83+
# Analyze crashes
84+
.PHONY: fuzz_crash
85+
fuzz_crash: build_fuzz
86+
@echo "🔍 Analyzing fuzzing crashes..."
87+
@$(PYTHON) $(FUZZ_DIR)/analyze_local_crashes.py
88+
89+
# Clean fuzzing artifacts
90+
.PHONY: fuzz_clean
91+
fuzz_clean:
92+
@echo "🧹 Cleaning fuzzing artifacts..."
93+
@rm -rf $(FUZZ_BUILD_DIR)
94+
@rm -rf $(FUZZ_DIR)/corpora
95+
@rm -rf $(FUZZ_DIR)/logs
96+
@rm -rf $(FUZZ_DIR)/coverage
97+
@echo "✅ Fuzzing artifacts cleaned"
98+
99+
# Generate coverage report
100+
.PHONY: fuzz_report
101+
fuzz_report:
102+
@if [ ! -d "$(FUZZ_COVERAGE_DIR)" ] || [ -z "$$(ls -A $(FUZZ_COVERAGE_DIR) 2>/dev/null)" ]; then \
103+
echo "Error: Coverage directory is empty. Please run the fuzzer with ENABLE_COVERAGE flag ON"; \
104+
exit 1; \
105+
fi
106+
@command -v llvm-profdata >/dev/null 2>&1 || { \
107+
echo "❌ Error: llvm-profdata not found. Please install LLVM tools."; \
108+
echo "On macOS: brew install llvm"; \
109+
echo "On Ubuntu: apt install llvm"; \
110+
exit 1; \
111+
}
112+
@if [ -z "$$(ls $(FUZZ_COVERAGE_DIR)/*.profraw 2>/dev/null)" ]; then \
113+
echo "❌ Error: No .profraw files found in coverage directory."; \
114+
echo "Please run fuzzer with coverage enabled first."; \
115+
exit 1; \
116+
fi
117+
@llvm-profdata merge -sparse $(FUZZ_COVERAGE_DIR)/*.profraw -o $(FUZZ_COVERAGE_DIR)/coverage.profdata
118+
@for fuzzer in $(FUZZ_BUILD_DIR)/fuzz-*; do \
119+
if [ -x "$$fuzzer" ]; then \
120+
echo "Coverage for $$(basename $$fuzzer):"; \
121+
llvm-cov report $$fuzzer -instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata; \
122+
fi; \
123+
done
124+
125+
# Generate unified HTML coverage report for all fuzzers
126+
.PHONY: fuzz_report_html
127+
fuzz_report_html:
128+
@# Check coverage directory exists and has data
129+
@if [ ! -d "$(FUZZ_COVERAGE_DIR)" ] || [ -z "$$(ls -A $(FUZZ_COVERAGE_DIR) 2>/dev/null)" ]; then \
130+
echo "Error: Coverage directory is empty. Please run the fuzzer with ENABLE_COVERAGE flag ON"; \
131+
exit 1; \
132+
fi
133+
@command -v llvm-profdata >/dev/null 2>&1 || { \
134+
echo "❌ Error: llvm-profdata not found. Please install LLVM tools."; \
135+
echo "On macOS: brew install llvm"; \
136+
echo "On Ubuntu: apt install llvm"; \
137+
exit 1; \
138+
}
139+
@command -v llvm-cov >/dev/null 2>&1 || { \
140+
echo "❌ Error: llvm-cov not found. Please install LLVM tools."; \
141+
echo "On macOS: brew install llvm"; \
142+
echo "On Ubuntu: apt install llvm"; \
143+
exit 1; \
144+
}
145+
@if [ -z "$$(ls $(FUZZ_COVERAGE_DIR)/*.profraw 2>/dev/null)" ]; then \
146+
echo "❌ Error: No .profraw files found in coverage directory."; \
147+
echo "Please run fuzzer with coverage enabled first."; \
148+
exit 1; \
149+
fi
150+
@# Merge all profraw files into a single profdata file
151+
@llvm-profdata merge -sparse $(FUZZ_COVERAGE_DIR)/*.profraw -o $(FUZZ_COVERAGE_DIR)/coverage.profdata
152+
@echo "Generating unified coverage report for all fuzzers..."
153+
@rm -rf $(FUZZ_COVERAGE_DIR)/report_html_unified
154+
@# Generate HTML report with all fuzzer binaries
155+
@llvm-cov show \
156+
$(FUZZ_BUILD_DIR)/fuzz-base58 \
157+
-object $(FUZZ_BUILD_DIR)/fuzz-base64 \
158+
-object $(FUZZ_BUILD_DIR)/fuzz-bech32 \
159+
-object $(FUZZ_BUILD_DIR)/fuzz-hexutils \
160+
-object $(FUZZ_BUILD_DIR)/fuzz-segwit_addr \
161+
-object $(FUZZ_BUILD_DIR)/fuzz-bignum \
162+
-object $(FUZZ_BUILD_DIR)/fuzz-zxformat \
163+
-object $(FUZZ_BUILD_DIR)/fuzz-timeutils \
164+
-instr-profile=$(FUZZ_COVERAGE_DIR)/coverage.profdata \
165+
-format=html \
166+
-output-dir=$(FUZZ_COVERAGE_DIR)/report_html_unified \
167+
-show-line-counts-or-regions \
168+
-show-instantiations \
169+
-show-expansions
170+
@echo "Unified HTML coverage report generated in $(FUZZ_COVERAGE_DIR)/report_html_unified"
171+
@# Open report in default browser (cross-platform)
172+
@open $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html 2>/dev/null || \
173+
xdg-open $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html 2>/dev/null || \
174+
echo "Please open manually: $(FUZZ_COVERAGE_DIR)/report_html_unified/index.html"
175+
176+
# === PYTHON FORMATTING TARGETS ===
177+
178+
# Find all Python files in fuzzing directories
179+
PYTHON_FILES := $(shell find $(FUZZING_DIR) -name "*.py" -type f -not -path "*/.*" 2>/dev/null)
37180

38181
# Install Black if not present
39-
.PHONY: install
40-
install:
182+
.PHONY: format_install
183+
format_install:
41184
@echo "🔧 Installing Black Python formatter..."
42-
@pip3 install black || { echo "❌ Failed to install Black"; exit 1; }
185+
@command -v black >/dev/null 2>&1 || \
186+
{ echo "🔧 Installing Black Python formatter…"; \
187+
$(PYTHON) -m pip install --user black || { echo "❌ Failed to install Black"; exit 1; }; }
43188
@echo "✅ Black installed successfully"
44189

45-
# Format all Python files (checks first, then formats only if needed)
190+
# Format all Python files
46191
.PHONY: format
47-
format: install validate_fuzz_dir
192+
format: format_install
48193
@echo "🎨 Formatting Python files with Black..."
49-
@if $(MAKE) check; then \
194+
@if [ -z "$(PYTHON_FILES)" ]; then \
195+
echo "⚠️ No Python files found"; \
196+
elif black --check --quiet --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES); then \
50197
echo "✅ All Python files are already properly formatted - no changes needed"; \
51198
else \
52-
echo "⚙️ Files need formatting - applying changes..."; \
53-
if [ -z "$(PYTHON_FILES)" ]; then \
54-
echo "⚠️ No Python files found"; \
55-
else \
56-
black --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES) && \
57-
echo "✅ All Python files formatted successfully" || \
58-
{ echo "❌ Formatting failed"; exit 1; }; \
59-
fi; \
199+
echo "⚙️ Applying formatting changes…"; \
200+
black --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES); \
60201
fi
61202

62203
# Check formatting without making changes
63204
.PHONY: check
64-
check: install validate_fuzz_dir
205+
check: format_install
65206
@echo "🔍 Checking Python file formatting..."
66207
@if [ -z "$(PYTHON_FILES)" ]; then \
67208
echo "⚠️ No Python files found"; \
68209
else \
69-
echo "📁 Checking $(shell echo $(PYTHON_FILES) | wc -w) Python files:"; \
70-
for file in $(PYTHON_FILES); do \
71-
echo "$$(basename $$file)"; \
72-
done; \
73-
echo ""; \
210+
echo "📁 Checking $(shell echo $(PYTHON_FILES) | wc -w) Python files"; \
74211
black --check --line-length $(BLACK_LINE_LENGTH) $(PYTHON_FILES) && \
75212
echo "✅ All Python files are properly formatted" || \
76213
{ echo "❌ Some files need formatting - run 'make format'"; exit 1; }; \
77214
fi
78215

79-
# List all Python files that would be formatted
80-
.PHONY: list
81-
list: validate_fuzz_dir
216+
# Clean Python cache files
217+
.PHONY: format_clean
218+
format_clean:
219+
@echo "🧹 Cleaning Python cache files..."
220+
@find $(FUZZING_DIR) -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
221+
@find $(FUZZING_DIR) -name "*.pyc" -delete 2>/dev/null || true
222+
@find $(FUZZING_DIR) -name "*.pyo" -delete 2>/dev/null || true
223+
@echo "✅ Python cache files cleaned"
224+
225+
# List Python files
226+
.PHONY: format_list
227+
format_list:
82228
@echo "Python files in fuzzing directories:"
83229
@if [ -z "$(PYTHON_FILES)" ]; then \
84-
echo " No Python files found in: $(FUZZING_DIR) $(PROJECT_FUZZ_DIR)"; \
230+
echo " No Python files found in: $(FUZZING_DIR)"; \
85231
else \
86232
for file in $(PYTHON_FILES); do \
87233
echo " $$file"; \
88234
done; \
89-
fi
90-
91-
# Clean Python cache files
92-
.PHONY: clean
93-
clean:
94-
@echo "🧹 Cleaning Python cache files..."
95-
@find $(PYTHON_DIRS) -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
96-
@find $(PYTHON_DIRS) -name "*.pyc" -delete 2>/dev/null || true
97-
@find $(PYTHON_DIRS) -name "*.pyo" -delete 2>/dev/null || true
98-
@echo "✅ Python cache files cleaned"
99-
100-
# Verify Black installation
101-
.PHONY: version
102-
version:
103-
@echo "🔧 Black version information:"
104-
@black --version 2>/dev/null || echo "❌ Black not installed - run 'make install'"
235+
fi

0 commit comments

Comments
 (0)