From 7220afffad72fa60d75c2e68248e9d978540d055 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Valencia Londono <¨valencialondonojuansebastian@gmail.com¨> Date: Mon, 20 Jul 2026 20:50:52 -0500 Subject: [PATCH 1/2] doc: update contributing.md --- CONTRIBUTING.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0628ab4..85b6634 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ Thank you for your interest in contributing to **tinyxml2-rs**! Every contributi - [Development Setup](#development-setup) - [Code Style](#code-style) - [Testing](#testing) + - [Fuzz Testing](#fuzz-testing) - [Pull Request Process](#pull-request-process) - [Commit Convention](#commit-convention) - [Architecture Reference](#architecture-reference) @@ -174,6 +175,101 @@ cargo test --workspace -- test_name cargo test --workspace -- --nocapture ``` +### Fuzz Testing + +Fuzz testing feeds random/mutated inputs to find crashes, panics, and invariant violations that unit tests miss. We use [`cargo-fuzz`](https://rust-fuzz.github.io/book/cargo-fuzz.html) (libFuzzer) with 4 targets in the `fuzz/` directory. + +#### Setup + +```bash +# Install cargo-fuzz (one-time) +cargo install cargo-fuzz +``` + +`cargo fuzz` manages its own nightly toolchain via `rustup`. Make sure `rustup` is installed. + +#### Fuzz Targets + +| Target | What it tests | +|--------|---------------| +| `parse_fuzz` | Feed arbitrary byte sequences to `Document::parse_bytes`. Must never panic. | +| `roundtrip_fuzz` | Parse → serialize → parse → assert output is stable (idempotent round-trip). | +| `serialize_fuzz` | Parse valid XML, then run `to_string` and `to_string_compact`. Must never panic. | +| `streaming_fuzz` | Feed random sequences of `XmlPrinter` method calls. Must never panic. | + +#### Running Fuzz Targets + +All commands run from the `fuzz/` directory: + +```bash +cd fuzz + +# Run a single target (runs indefinitely — Ctrl+C to stop) +cargo fuzz run parse_fuzz + +# Run with a time limit (recommended for PR validation) +cargo fuzz run parse_fuzz -- -max_total_time=300 + +# Run other targets +cargo fuzz run roundtrip_fuzz +cargo fuzz run serialize_fuzz +cargo fuzz run streaming_fuzz + +# Run all four targets sequentially (5 minutes each) +for target in parse_fuzz roundtrip_fuzz serialize_fuzz streaming_fuzz; do + echo "=== Fuzzing $target ===" + cargo fuzz run "$target" -- -max_total_time=300 +done +``` + +**Useful flags** (passed after `--`): + +| Flag | Purpose | +|------|---------| +| `-max_total_time=N` | Stop after N seconds | +| `-max_len=N` | Limit input size to N bytes | +| `-jobs=N` | Run N parallel fuzzer jobs | +| `-dict=path/to/dict` | Use a dictionary of known tokens | +| `-rss_limit_mb=N` | Limit memory usage per job to N MB | + +#### Reproducing a Crash + +When a fuzzer finds a crash, it saves the crashing input as an artifact in `fuzz/artifacts//`. + +```bash +# Reproduce the crash with the saved artifact +cargo fuzz run parse_fuzz fuzz/artifacts/parse_fuzz/ + +# To minimize the crashing input (find the smallest input that still triggers the bug) +cargo fuzz cmin parse_fuzz fuzz/artifacts/parse_fuzz/ +``` + +The output will show the panic message, stack trace, and exact input bytes that triggered the crash. Use this to write a minimal reproduction as a unit test, then fix the bug. + +#### How Long to Fuzz + +For a **quick smoke test** before opening a PR: **5 minutes per target** (20 minutes total) is the minimum. + +For changes to **parsing logic**, **serialization**, or **the `XmlPrinter` API**: run the relevant target for at least **30 minutes**. These are the highest-risk areas where subtle edge cases hide. + +For a **new release**: run all 4 targets for **2+ hours each** (overnight is ideal). Fuzzing is embarrassingly parallel — run each target in its own terminal: + +```bash +# Terminal 1 +cd fuzz && cargo fuzz run parse_fuzz -- -max_total_time=7200 + +# Terminal 2 +cd fuzz && cargo fuzz run roundtrip_fuzz -- -max_total_time=7200 + +# Terminal 3 +cd fuzz && cargo fuzz run serialize_fuzz -- -max_total_time=7200 + +# Terminal 4 +cd fuzz && cargo fuzz run streaming_fuzz -- -max_total_time=7200 +``` + +> **Tip:** If you're hunting for a known class of bug, create a [dictionary file](https://rust-fuzz.github.io/book/cargo-fuzz/structure-aware-fuzzing.html) with XML tokens (`<`, `>`, ``, ` Date: Tue, 21 Jul 2026 08:02:33 -0500 Subject: [PATCH 2/2] doc: Add Setup and Reproducing a Crash suggetions from coderabbit review --- CONTRIBUTING.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85b6634..7925b36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -181,13 +181,19 @@ Fuzz testing feeds random/mutated inputs to find crashes, panics, and invariant #### Setup +Fuzz targets require a **nightly Rust toolchain** and a **C++11-capable compiler** (GCC or Clang) for the sanitizer runtime. + ```bash +# Install the nightly toolchain (one-time) +rustup toolchain install nightly + +# Optionally, set nightly as the default for this project +rustup override set nightly + # Install cargo-fuzz (one-time) cargo install cargo-fuzz ``` -`cargo fuzz` manages its own nightly toolchain via `rustup`. Make sure `rustup` is installed. - #### Fuzz Targets | Target | What it tests | @@ -234,14 +240,14 @@ done #### Reproducing a Crash -When a fuzzer finds a crash, it saves the crashing input as an artifact in `fuzz/artifacts//`. +When a fuzzer finds a crash, it saves the crashing input as an artifact in `artifacts//`. ```bash # Reproduce the crash with the saved artifact -cargo fuzz run parse_fuzz fuzz/artifacts/parse_fuzz/ +cargo fuzz run parse_fuzz artifacts/parse_fuzz/ -# To minimize the crashing input (find the smallest input that still triggers the bug) -cargo fuzz cmin parse_fuzz fuzz/artifacts/parse_fuzz/ +# Minimize the crashing input (find the smallest input that still triggers the bug) +cargo fuzz tmin parse_fuzz artifacts/parse_fuzz/ ``` The output will show the panic message, stack trace, and exact input bytes that triggered the crash. Use this to write a minimal reproduction as a unit test, then fix the bug.