Skip to content

(03) fuzz 2 - #1798

Draft
daniel-noland wants to merge 20 commits into
pr/daniel-noland/fuzz-nf-probesfrom
pr/daniel-noland/clock-facade
Draft

(03) fuzz 2#1798
daniel-noland wants to merge 20 commits into
pr/daniel-noland/fuzz-nf-probesfrom
pr/daniel-noland/clock-facade

Conversation

@daniel-noland

Copy link
Copy Markdown
Collaborator

No description provided.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 62fc094 to cd223db Compare September 3, 2026 05:53
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch 2 times, most recently from 7659dc6 to c5e678d Compare September 3, 2026 05:59
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from cd223db to 695d16e Compare September 3, 2026 05:59
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 695d16e to e3b65c1 Compare September 3, 2026 06:22
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch 2 times, most recently from adff246 to bda85f1 Compare September 3, 2026 07:02
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from e3b65c1 to 29b583b Compare September 3, 2026 07:02
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from bda85f1 to 2800ab4 Compare September 3, 2026 20:06
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch 2 times, most recently from 1887ca0 to f3b2a1c Compare September 3, 2026 20:32
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch 2 times, most recently from 9683625 to 7251007 Compare September 3, 2026 23:04
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch 2 times, most recently from 1ad13aa to 180c6e0 Compare September 6, 2026 04:39
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from 7251007 to 3c13e1e Compare September 6, 2026 04:39
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 180c6e0 to f4731dc Compare September 6, 2026 06:15
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from 252d3cb to 91f1816 Compare September 6, 2026 18:30
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from f4731dc to 15fe5eb Compare September 6, 2026 18:30
@daniel-noland daniel-noland changed the title (03) clock (03) fuzz 2 Sep 6, 2026
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 15fe5eb to 60d94b2 Compare September 6, 2026 19:05
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch 2 times, most recently from 440fd4c to c572452 Compare September 6, 2026 19:31
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 60d94b2 to aee8206 Compare September 6, 2026 19:31
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from c572452 to e6e3e56 Compare September 8, 2026 02:49
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from aee8206 to 7686872 Compare September 8, 2026 02:49
@daniel-noland daniel-noland self-assigned this Sep 8, 2026
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from e6e3e56 to 0259d67 Compare September 8, 2026 06:58
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 7686872 to 34a0918 Compare September 8, 2026 06:58
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from 0259d67 to 15b3652 Compare September 8, 2026 07:04
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from 34a0918 to a021558 Compare September 8, 2026 07:04
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/fuzz-nf-probes branch from 15b3652 to 7e125a1 Compare September 8, 2026 23:49
daniel-noland and others added 20 commits September 8, 2026 18:11
Timeout code read the wall clock while Tokio timers used a pausable clock.
After virtual time advanced, new deadlines could already be expired, and
meaningful expiry tests otherwise required slow, flaky real-time sleeps.

Add a clock facade that uses `std` in production and Tokio time under the
`virtual` feature. Route monotonic and system clock reads through it,
migrate existing call sites, and add a Semgrep rule that prevents direct
clock reads from recreating the mismatch.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Flow expiry had no deterministic coverage because each case required real
timeout delays. Refreshing a flow under mismatched clocks could also move
its deadline into the past.

Use virtual time to verify that live flows survive, expired flows are
removed without leaking untranslated traffic, refreshes extend lifetime,
and later flows are not born expired. Add an ignored reproduction showing
that a live tuple can be reissued when only one half of its pair remains
refreshed; the defect is fixed separately.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Configuration checks did not exercise destination translation in the
port-forwarding stage, and independently chosen probe protocols missed
their rules about half the time.

Resolve packets from generated rules and check reversibility, containment,
injectivity, permission, unchanged fields, stability, and expiry. Generate
disjoint exposes with one protocol per rule, and bound injectivity sweeps
so each run covers several configurations.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`FlowInfo` controls lifetime and state for every stateful NAT flow but had
no direct tests. A deadline moving backward, a partial failed refresh, or
mismatched pair metadata can silently retire or misroute active flows.

Generate operation sequences and check invariants after every prefix:
monotonic deadlines, exact refresh permissions, idempotent invalidation,
timer cancellation, related-pair identity, initiator uniqueness, status
encoding, and partner metadata.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Masquerade refreshed only the flow entry a packet hit. Mostly one-way
traffic could let the partner expire while still translating, releasing
its public tuple for another tenant and creating a cross-tenant collision.

Refresh both entries whenever either direction carries traffic. Properties
now require one-sided traffic to keep the pair alive and prevent a live
flow's tuple from being reissued.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every reported rate passes through the exponentially weighted moving
average, but it had no tests. Time weighting and floating-point boundaries
could therefore distort traffic rates without producing an error.

Verify that the first sample is exact, averages remain within observed
bounds, constant input stays constant, steps converge without overshoot,
longer gaps weight new samples more, and reads do not mutate state. Bound
strict comparisons away from floating-point saturation.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The gRPC statistics store had no tests, so wrapped counters, stale names,
incorrect pair attribution, or over-aggressive pruning could produce
plausible but wrong operator data.

Drive generated sequences of updates, pruning, and snapshots. Check
saturating monotonic counters, equivalence of compound and individual
updates, independence of VPC and pair tables, exact live-set pruning, and
removal of names with their VPCs.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`TimeSlice::split_count` assigns packet counts across reporting-window
boundaries and had no coverage. Arithmetic errors here silently lose or
duplicate traffic in published statistics.

Verify conservation across every split, monotonic allocation for non-empty
overlaps, complete carryover after a window, and deliberate recovery of
late samples from before the current window. Record the zero-duration
boundary that avoids division by zero.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 60-second stale-route window was effectively untested. Incorrect
boundaries can preserve routes withdrawn during an FRR restart or remove
live routes before FRR has republished them.

Drive the clock to verify arming, the exact deadline boundary, one-shot
expiry, unarmed behavior, deleted-VRF lifetime, restart transitions, and
settled CPI states. Virtual time makes the full window deterministic and
immediate.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The router I/O loop was tested only by starting it and sleeping, leaving
socket readiness, decoding, dispatch, replies, malformed input, and CLI
recovery largely uncovered.

Add real Unix-datagram peers and a scoped loop fixture. Verify the locked
CPI behavior, queued requests after unlock, connect and refusal paths,
malformed datagram recovery, and CLI service after its socket path is
removed. Give every test unique paths to avoid interfering with concurrent
tests or a running dataplane.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The frrmi receiver treated resized buffer capacity as received data, so it
could parse incomplete headers or bodies and had no bound on announced
message size.

Track the used buffer length, wait for complete headers and bodies, and
reject frames larger than 16 MiB.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Framing tests did not cover the router loop around frrmi. A refused startup
connection, an agent restart, or malformed input could leave the dataplane
permanently unable to send FRR configuration.

Stand in for `frr-agent` with a Unix listener and require the loop to
connect when the agent appears, reconnect after disconnect, and restart
the link after invalid framing.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Earlier socket tests covered guards and refusals but never carried an
accepted route through decode, RIB reconciliation, and FIB publication.
Configuration exchange and large chunked replies were also unchecked.

Configure a minimal router and verify route addition, withdrawal, duplicate
announcements, frrmi configuration acknowledgements, and an 8,192-route
CLI response. Exclude the write-backpressure path because reaching its
16 MiB socket buffer would make the unit test disproportionate.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven `FlowInfo` properties still allowed nineteen meaningful
cargo-mutants changes to survive, including the exact deadline boundary
used by masquerade expiry.

Add checks for exact reset and extension deadlines, active-state behavior,
constructor status, generation IDs, partner propagation, individual flag
predicates, and destination VPC storage. Exclude printers, generator
contracts, and privileged sysfs code from mutation scoring. No meaningful
mutants remain in the file.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Existing sequence tests missed most mutations to masquerade's protocol
transition guards. Premature or missing close states change allocation
lifetime and can release a tuple while a connection is still active.

Enumerate all 320 TCP action, status, and flag combinations against an
explicit transition table. Separately verify inert flag sets, reset
absorption, DNS-over-UDP close behavior, and ICMP reply transitions. No
meaningful mutants remain in `protocol.rs`.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Extra blank lines failed the repository's formatting gate, and the final
direct `std::time::Instant` import made it easy to bypass the new clock
facade despite passing the call-site lint.

Apply rustfmt and route the remaining import through `clock`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Port-forwarding properties ran their zero-count vacuity guard after
Bolero returned from target discovery inside a closure. Target enumeration
failed before any property could be selected for fuzzing.

Detect discovery before enforcing property-run yield checks. Selecting all
204 NAT crate targets now succeeds.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
The port-forwarding properties inherited an absolute sample floor that
measures machine speed and can fail under slow instrumentation despite a
healthy input distribution.

Keep the proportional yield check and nonzero requirement without a fixed
execution-count threshold.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
`a_large_answer_arrives_whole` took 31.06 seconds on a loaded debug runner
and exceeded its 30-second timeout. The deadline is a hung-subsystem
backstop rather than a performance assertion.

Raise the non-instrumented timeout to 120 seconds, leaving the existing
300-second instrumented timeout unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Daniel Noland <daniel@githedgehog.com>
The static rio test counter used `concurrency::sync::AtomicUsize`, which
becomes Loom's non-const atomic under the `loom` feature. Workspace
all-target checks therefore failed to compile before any model test ran.

Use `concurrency::process_global::AtomicUsize`; the counter belongs to the
test process and is never part of a model-checker execution.

Signed-off-by: Daniel Noland <daniel@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@daniel-noland
daniel-noland force-pushed the pr/daniel-noland/clock-facade branch from a021558 to 0417969 Compare September 9, 2026 00:12
@daniel-noland daniel-noland added the ci:+merge-ready Run all checks which will be run in the merge queue regardless of label status label Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:+merge-ready Run all checks which will be run in the merge queue regardless of label status

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant