MXC uses cargo-fuzz for local fuzzing harnesses and OneFuzz for continuous fuzzing in CI.
The mxc_fuzz crate at src/testing/fuzz/ defines three libFuzzer targets, all
exercising the attacker-influenced config surface consumed by wxc-exec and
lxc-exec:
| Target | Entry point |
|---|---|
config_parser |
load_mxc_request(s, .., is_base64 = false) |
base64_decode |
load_mxc_request(s, .., is_base64 = true) (SDK wire format) |
validator |
parse + validate_common on a one-shot request |
Seed corpora for config_parser and validator targets come directly from
tests/configs/*.json. The base64_decode target uses pre-encoded seeds in
src/testing/fuzz/corpus/base64_decode/. OneFuzz dedups by coverage server-side and
grows the corpus across daily runs, so we keep the in-repo seeds small.
Targets are pure-Rust code in wxc_common, so they compile and run
identically on Windows, Linux, and macOS. We fuzz on Windows only
because:
- OneFuzz supports Windows, Ubuntu, AzureLinux3, and TKO — not macOS.
- For these parser targets the bugs are platform-independent; one OS gives full coverage of the relevant code paths.
# One-time setup
rustup toolchain install nightly --profile minimal
cargo +nightly install cargo-fuzz
# Put the MSVC ASAN runtime DLL on PATH for this shell
$asanDir = (Get-ChildItem 'C:\Program Files\Microsoft Visual Studio' -Recurse `
-Filter 'clang_rt.asan_dynamic-x86_64.dll' -ErrorAction SilentlyContinue `
| Where-Object FullName -Match 'HostX64\\x64\\clang_rt' | Select-Object -First 1).Directory.FullName
$env:PATH = "$asanDir;$env:PATH"
# Run a target for 30 seconds (uses tests/configs/ as the seed corpus)
cd src\testing\fuzz
cargo +nightly fuzz run config_parser ..\..\tests\configs -- -max_total_time=30Discovered crashes are written to artifacts/<target>/ (relative to src/testing/fuzz/)
and printed to the console. Re-run a single input with:
cargo +nightly fuzz run config_parser artifacts\config_parser\crash-<hash>libFuzzer auto-saves any new-coverage input into the corpus dir during a run, which can bloat the commit. Before committing seed-corpus updates:
cargo +nightly fuzz cmin <target>cmin keeps the smallest set that retains all coverage.
.azure-pipelines/Fuzz.Build.yml runs daily at 00:00 UTC on main. The job
template (.azure-pipelines/templates/Fuzz.Build.Job.yml):
- Installs nightly Rust on the agent (cargo registry stays pointed at
Mxc-Azure-Feed, so all crate sources still come from the internal feed). - Installs
cargo-fuzz. - Builds the three fuzz targets with
-Z sanitizer=address. - Stages an OneFuzz drop directory. The OneFuzzConfig v3 schema requires
a flat layout:
OneFuzzConfig.jsonat the drop root with each fuzzer's.exe/.pdband the shared ASAN runtime DLL alongside it (referenced by simple name in each entry'sFuzzer.FuzzingHarnessExecutableNameandJobDependencies). Seeds are not shipped in the drop in v3 — they must live in an Azure storage container referenced viaOneFuzzJobs[].SeedCorpusContainer(onefuzz containers create mxc-seeds-<target>+ SAS upload). Until those containers are provisioned, fuzzers bootstrap their corpus from scratch. - Publishes the drop dir as a pipeline artifact (for debugging).
- Submits via
onefuzz-task@0(skipped on PR builds).
When OneFuzz files a bug via the routing configured in OneFuzzConfig.json,
triage steps:
- Reproduce locally. Download the offending input from the fuzz job
page and run
cargo +nightly fuzz run <target> <crash-file>(see "Running locally"). If it reproduces againstmain, the bug is real. - Classify. AddressSanitizer findings (heap overflow, use-after-free, etc.) are security-relevant and should be handled through the project's security response process. Plain panics in parsers are correctness bugs and can be fixed in-band.
- Add a regression test. Drop the minimized crash input into the
appropriate corpus subdir so
cminkeeps it. If the bug fits the unit test pattern, add a dedicated#[test]inwxc_commontoo. - Fix + verify. After the fix lands, re-run the fuzz target locally against the original crash to confirm. Once the daily pipeline runs again with the fix, the fuzz job should mark the bug as resolved.