Skip to content

Commit 8c7ad9f

Browse files
randyquayeclaude
andcommitted
feat: migrate fuzzing container to ContFuzzer v2 interface
Replaces the v1 CLI-flag interface (--fuzzer, --mode, --asm) with v2's env-var contract (FUZZ_TARGET, FUZZ_MODE, FUZZ_JOBS, etc). Each variant (asm, noasm, avm) is now an independent target under /targets/ so the platform can schedule them as separate campaigns. - Dockerfile: flattens all variant binaries into /targets/<name>[_suffix] - entrypoint.sh: reads FUZZ_* env vars, dispatches fuzz/coverage/minimize/reproduce - run.sh: updated local runner with --target flag - merge_fuzzer_manifests_v2.py: outputs schema v2 manifest for ORAS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 590a56f commit 8c7ad9f

File tree

6 files changed

+356
-516
lines changed

6 files changed

+356
-516
lines changed

barretenberg/cpp/scripts/merge_fuzzer_manifests.py

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
"""Merge per-preset fuzzer manifests into ContFuzzer v2 schema.
3+
4+
Reads per-preset manifest JSON files (generated by cmake) and produces a
5+
unified manifest with schema v2 that ContFuzzer v2 consumes via ORAS.
6+
7+
Each (fuzzer, variant) pair that should be independently scheduled becomes
8+
a target. Companion binaries (asan, cov) are NOT listed — the entrypoint
9+
discovers them automatically by suffix convention.
10+
"""
11+
12+
import argparse
13+
import json
14+
import os
15+
import sys
16+
17+
18+
def main():
19+
parser = argparse.ArgumentParser(description="Merge per-preset fuzzer manifests (v2 schema)")
20+
parser.add_argument("manifests", nargs="+", help="Per-preset manifest JSON files")
21+
parser.add_argument("--output", required=True, help="Output merged manifest path")
22+
parser.add_argument("--cpus", type=int, default=4, help="Default CPU allocation per target")
23+
parser.add_argument("--memory-gb", type=int, default=8, help="Default memory (GB) per target")
24+
args = parser.parse_args()
25+
26+
# Collect fuzzers per preset
27+
preset_fuzzers: dict[str, dict[str, str]] = {}
28+
for path in args.manifests:
29+
if not os.path.exists(path):
30+
print(f"Warning: {path} not found, skipping", file=sys.stderr)
31+
continue
32+
with open(path) as f:
33+
manifest = json.load(f)
34+
preset = manifest.get("preset", "unknown")
35+
for fuzzer in manifest.get("fuzzers", []):
36+
preset_fuzzers.setdefault(preset, {})[fuzzer["name"]] = fuzzer["source_path"]
37+
38+
cov_names = set(preset_fuzzers.get("fuzzing-coverage", {}).keys())
39+
40+
# Schedulable variants: (preset, suffix, label)
41+
schedulable = [
42+
("fuzzing", "", "asm"),
43+
("fuzzing-noasm", "_noasm", "noasm"),
44+
("fuzzing-avm", "_avm", "avm"),
45+
]
46+
47+
targets = []
48+
for preset, suffix, label in schedulable:
49+
fuzzers = preset_fuzzers.get(preset, {})
50+
for name, source_path in sorted(fuzzers.items()):
51+
modes = ["fuzz", "minimize", "reproduce"]
52+
if name in cov_names:
53+
modes.append("coverage")
54+
55+
targets.append({
56+
"name": f"{name}{suffix}",
57+
"display_name": f"{name} ({label})",
58+
"language": "c++",
59+
"fuzzer_engine": "libfuzzer",
60+
"source_path": f"barretenberg/cpp/src/barretenberg/{source_path}",
61+
"modes": modes,
62+
"resources": {"cpus": args.cpus, "memory_gb": args.memory_gb},
63+
})
64+
65+
output = {"schema": "2", "targets": targets}
66+
67+
os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
68+
with open(args.output, "w") as f:
69+
json.dump(output, f, indent=2)
70+
71+
print(f"Merged {len(targets)} targets into {args.output} (schema v2)")
72+
73+
74+
if __name__ == "__main__":
75+
main()
Lines changed: 45 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,77 @@
11
#!/usr/bin/env bash
2+
# Local runner for the ContFuzzer v2 fuzzing container.
3+
# Builds the image and runs a target using the v2 env-var interface.
24

35
set -euo pipefail
46
IFS=$'\n\t'
57

6-
fuzzer=''
7-
verbosity='0'
8-
timeout='2592000' # 1 month
9-
mode='fuzzing'
10-
asm='on'
8+
target=''
9+
mode='fuzz'
10+
timeout='3600'
1111
cpus='8'
12-
mem="16G"
12+
mem='16G'
1313
jobs_="$cpus"
14-
workers='0'
15-
avm='off'
16-
rss_limit='2048 '
14+
rss_limit='2048'
1715

1816
show_help() {
1917
echo "Usage: $0 [options]"
2018
echo ""
2119
echo "Options:"
22-
echo " -v, --verbose Enable fuzzer's verbose mode (default: disabled)"
23-
echo " -f, --fuzzer <fuzzer_name> Specify the fuzzer to use (current: $fuzzer)"
24-
echo " -t, --timeout <timeout> Set the maximum total time for fuzzing in seconds (default: $timeout - 1 month)"
25-
echo " -c, --cpus <cpus> Set the amount of CPUs for container to use (default: $cpus)"
26-
echo " --mem <memory> Set the amount of memory for container to use (default: $mem)"
27-
echo " -j, --jobs <N> Set the amount of processes to run (default: $jobs_)"
28-
echo " -w, --workers <N> Set the amount of subprocesses per job (default: $workers)"
29-
echo " -m, --mode <mode> Set the mode of operation (fuzzing, coverage or regress-only) (default: $mode)"
30-
echo " -a, --asm <mode> Set the flag to enable/disable asm instructions (on/off) (default: $asm)"
31-
echo " -A, --avm Enable AVM fuzzing mode (uses build-fuzzing-avm) (default: $avm)"
32-
echo " -r, --rss-limit <MB> Set RSS limit in megabytes (default: 2048 MB)"
33-
echo " -h, --help Display this help and exit"
34-
echo " --show-fuzzers Display the available fuzzers"
35-
echo ""
36-
echo "This script handles fuzzing testing with specified parameters, managing crash reports,"
37-
echo "and coverage testing based on the mode specified."
20+
echo " -t, --target <name> Target name (from /targets/), e.g. bigfield_fuzzer"
21+
echo " -m, --mode <mode> fuzz | coverage | minimize | reproduce (default: $mode)"
22+
echo " --timeout <secs> Fuzzing timeout in seconds (default: $timeout)"
23+
echo " -c, --cpus <n> CPU allocation (default: $cpus)"
24+
echo " --mem <size> Memory limit (default: $mem)"
25+
echo " -j, --jobs <n> Parallelism (default: $jobs_)"
26+
echo " -r, --rss-limit <MB> RSS limit in MB (default: $rss_limit)"
27+
echo " --list-targets List available targets and exit"
28+
echo " -h, --help Show this help"
3829
}
3930

31+
list_targets=0
32+
4033
while [[ $# -gt 0 ]]; do
4134
case "$1" in
42-
-v | --verbose)
43-
verbosity="1"
44-
shift
45-
;;
46-
-f | --fuzzer)
47-
fuzzer="$2"
48-
shift 2
49-
;;
50-
--show-fuzzers)
51-
mode="show-fuzzers"
52-
shift
53-
;;
54-
-t | --timeout)
55-
timeout="$2"
56-
shift 2
57-
;;
58-
-w | --workers)
59-
workers="$2"
60-
shift 2
61-
;;
62-
-j | --jobs)
63-
jobs_="$2"
64-
shift 2
65-
;;
66-
-m | --mode)
67-
mode="$2"
68-
shift 2
69-
;;
70-
-a | --asm)
71-
asm="$2"
72-
shift 2
73-
;;
74-
-A | --avm)
75-
avm='on'
76-
shift
77-
;;
78-
-r | --rss-limit)
79-
rss_limit="$2"
80-
shift 2
81-
;;
82-
-c | --cpus)
83-
cpus="$2"
84-
shift 2
85-
;;
86-
--mem)
87-
mem="$2"
88-
shift 2
89-
;;
90-
-h | --help)
91-
show_help
92-
exit 0
93-
;;
94-
--)
95-
shift
96-
break
97-
;;
98-
-*)
99-
echo "Error: Unsupported flag $1" >&2
100-
exit 1
101-
;;
102-
*)
103-
break
104-
;;
35+
-t | --target) target="$2"; shift 2 ;;
36+
-m | --mode) mode="$2"; shift 2 ;;
37+
--timeout) timeout="$2"; shift 2 ;;
38+
-c | --cpus) cpus="$2"; jobs_="$cpus"; shift 2 ;;
39+
--mem) mem="$2"; shift 2 ;;
40+
-j | --jobs) jobs_="$2"; shift 2 ;;
41+
-r | --rss-limit) rss_limit="$2"; shift 2 ;;
42+
--list-targets) list_targets=1; shift ;;
43+
-h | --help) show_help; exit 0 ;;
44+
*) echo "Unknown flag: $1" >&2; exit 1 ;;
10545
esac
10646
done
10747

10848
image_name=barretenberg-fuzzer
10949

11050
docker build src/ -t "$image_name":latest
11151

112-
if [[ "$mode" == "show-fuzzers" ]]; then
113-
entrypoint_args=(--show-fuzzers)
114-
entrypoint_args+=(--asm "$asm")
115-
entrypoint_args+=(--avm "$avm")
116-
117-
docker run -it --rm \
118-
--entrypoint "./entrypoint.sh" \
119-
"$image_name" \
120-
"${entrypoint_args[@]}"
52+
if [[ "$list_targets" -eq 1 ]]; then
53+
docker run --rm "$image_name" ls /targets/
12154
exit 0
12255
fi
12356

124-
if [ -z "${fuzzer}" ]; then
125-
echo "err: No fuzzer was provided"
126-
echo
57+
if [ -z "$target" ]; then
58+
echo "err: No target specified. Use --target <name> or --list-targets" >&2
12759
show_help
12860
exit 1
12961
fi
13062

131-
mkdir -p crash-reports/unsorted output corpus coverage artifacts
63+
mkdir -p corpus crashes output
13264

133-
docker_args=(
134-
-it --rm
135-
--user root
136-
-v "$(pwd)/crash-reports:/home/fuzzer/crash-reports:rw"
137-
-v "$(pwd)/output:/home/fuzzer/output:rw"
138-
-v "$(pwd)/corpus:/home/fuzzer/corpus:rw"
139-
-v "$(pwd)/coverage:/home/fuzzer/coverage:rw"
140-
-v "$(pwd)/artifacts:/home/fuzzer/artifacts:rw"
141-
--cpus="$cpus"
142-
-m "$mem"
143-
--entrypoint "./entrypoint.sh"
65+
docker run -it --rm \
66+
--user root \
67+
--cpus="$cpus" \
68+
-m "$mem" \
69+
-e "FUZZ_TARGET=$target" \
70+
-e "FUZZ_MODE=$mode" \
71+
-e "FUZZ_TIMEOUT=$timeout" \
72+
-e "FUZZ_JOBS=$jobs_" \
73+
-e "FUZZ_RSS_LIMIT=$rss_limit" \
74+
-v "$(pwd)/corpus:/corpus:rw" \
75+
-v "$(pwd)/crashes:/crashes:rw" \
76+
-v "$(pwd)/output:/output:rw" \
14477
"$image_name"
145-
)
146-
147-
entrypoint_args=(
148-
--fuzzer "$fuzzer"
149-
--mode "$mode"
150-
--asm "$asm"
151-
--avm "$avm"
152-
--timeout "$timeout"
153-
--workers "$workers"
154-
--jobs "$jobs_"
155-
--verbosity "$verbosity"
156-
--rss-limit "$rss_limit"
157-
)
158-
159-
docker run "${docker_args[@]}" "${entrypoint_args[@]}"

container-builds/fuzzing-container/src/Dockerfile

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,28 @@ RUN cmake --build build-fuzzing-cov
6565
RUN cmake -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 --preset fuzzing-avm
6666
RUN cmake --build build-fuzzing-avm && mv build-fuzzing-avm/bin bin-fuzzing-avm && rm -rf build-fuzzing-avm
6767

68-
# Merge per-preset manifests into unified manifest
69-
RUN python3 scripts/merge_fuzzer_manifests.py \
68+
# Generate v2 manifest from per-preset manifests
69+
RUN python3 scripts/merge_fuzzer_manifests_v2.py \
7070
bin-fuzzing/fuzzer_manifest.json \
7171
bin-fuzzing-noasm/fuzzer_manifest.json \
7272
bin-fuzzing-avm/fuzzer_manifest.json \
7373
build-fuzzing-cov/bin/fuzzer_manifest.json \
7474
--output /tmp/fuzzer_manifest.json
7575

76+
# Flatten all variant binaries into /targets/<name>[_variant]
77+
RUN mkdir -p /targets && \
78+
for f in bin-fuzzing/*; do \
79+
name=$(basename "$f"); \
80+
[ -x "bin-fuzzing/$name" ] && cp "bin-fuzzing/$name" "/targets/${name}"; \
81+
[ -x "bin-fuzzing-noasm/$name" ] && cp "bin-fuzzing-noasm/$name" "/targets/${name}_noasm" 2>/dev/null || true; \
82+
[ -x "bin-fuzzing-asan/$name" ] && cp "bin-fuzzing-asan/$name" "/targets/${name}_asan" 2>/dev/null || true; \
83+
[ -x "build-fuzzing-cov/bin/$name" ] && cp "build-fuzzing-cov/bin/$name" "/targets/${name}_cov" 2>/dev/null || true; \
84+
done && \
85+
for f in bin-fuzzing-avm/*; do \
86+
name=$(basename "$f"); \
87+
[ -x "bin-fuzzing-avm/$name" ] && cp "bin-fuzzing-avm/$name" "/targets/${name}_avm" 2>/dev/null || true; \
88+
done
89+
7690
# Runtime stage
7791
FROM ubuntu:noble AS runtime
7892

@@ -99,20 +113,18 @@ COPY --from=builder /home/fuzzer/aztec-packages-src /home/fuzzer/aztec-packages
99113

100114
WORKDIR /home/fuzzer/aztec-packages/barretenberg/cpp
101115

102-
# Copy binaries from build stage
103-
COPY --from=builder /home/fuzzer/aztec-packages/barretenberg/cpp/bin-fuzzing ./build-fuzzing/bin
104-
COPY --from=builder /home/fuzzer/aztec-packages/barretenberg/cpp/bin-fuzzing-noasm ./build-fuzzing-noasm/bin
105-
COPY --from=builder /home/fuzzer/aztec-packages/barretenberg/cpp/bin-fuzzing-asan ./build-fuzzing-asan/bin
106-
COPY --from=builder /home/fuzzer/aztec-packages/barretenberg/cpp/bin-fuzzing-avm ./build-fuzzing-avm/bin
107-
108116
# Copy full cov-build to keep LLVM links and external dependencies for coverage report
109117
COPY --from=builder /home/fuzzer/aztec-packages/barretenberg/cpp/build-fuzzing-cov/ ./build-fuzzing-cov/
110118

111119
# Copy CRS
112120
COPY --from=builder /root/.bb-crs /root/.bb-crs
113121

122+
# Copy flattened target binaries
123+
COPY --from=builder /targets/ /targets/
124+
114125
# Copy fuzzer manifest
115126
COPY --from=builder /tmp/fuzzer_manifest.json ./fuzzer_manifest.json
116127

117128
# Copy entrypoint script
118129
COPY entrypoint.sh .
130+
ENTRYPOINT ["./entrypoint.sh"]

0 commit comments

Comments
 (0)