Skip to content

Make FreeUnit's state machines bulletproof: FSM contracts + systematic flow/resource audits #33

Description

@andypost

Why

Most of this codebase's control flow runs on implicit state machines — state encoded in which handler pointer is installed (nxt_conn_state_t, 25+ instances), in flag combinations (timer enabled/queued/change, h1p bits), or in counter invariants (router nxt_app_t). A code survey found 20 distinct FSMs; only 4 are explicit enums. History shows this is where the bugs live: the chunked-relay proxy fix (nginx#72), the port-IPC reply-path leaks (71fcd89), the CI-only vanishing-headers flake (uninitialized field flags in the resumable parser), and the io_uring engine work's edge-delivery wedges — every one was a missing/undocumented state transition or an ownership gap on an error exit.

The one documentation format with a local track record is docs/io_uring/engine-contract.md (currently on the #31 branch): a code-verified prose contract with per-operation semantics, invariants, and a mismatch inventory — it predicted two real bugs (oneshot accept re-arm, consumed-wakeup readiness loss) before the engine existed. This issue proposes extending that bar to the whole codebase.

Proposal (full plan in the gist)

Plan: https://gist.github.com/andypost/6d47c38db20a977dc5edc62589010b63

Highlights:

  • Inventory: 20 FSMs ranked by risk (state storage file:line, explicit vs implicit, transition-site dispersion, resources keyed to each state). High-risk: conn lifecycle + 3-phase teardown, port send/recv + shm chunk bitmaps, h1proto client/peer pairs, process/app lifecycle, timers (deferred-change queue).
  • Format: layered — engine-contract-style prose contracts under docs/fsm/ as the normative spine; a machine-readable YAML transition block per doc; Mermaid diagrams generated from the YAML (never hand-drawn); NXT_DEBUG-gated assertions citing invariant IDs as living documentation (later, small PRs); TLA+/spin for exactly two machines — port messaging and conn close/free — where concurrency meets refcounting.
  • Audit techniques, ranked by expected bugs-per-effort: transition-completeness audits (every state × event cell gets a verdict — the io_uring wedges were exactly undefined cells); resource-ownership tracing per exit path (the 71fcd89 class); a trace-replay checker that replays debug-build pytest logs against the documented FSM and flags illegal transitions — the only technique that would have caught the CI-only parser flake; targeted fault injection extending the PR SIGSEGV in unit controller process on CentOS 6.9 nginx/unit#57 scaffolding.
  • Review gate: every doc independently re-derived from code by a second reviewer and diffed; every claim carries file:line. A doc failing verification is itself a finding.
  • Pilots: (A) unified conn lifecycle + fd-event + timer + accept contract; (B) router↔app port messaging incl. shm chunks and the RPC cancel matrix. Acceptance includes retro-predicting the known historical bugs in each area and a zero-illegal-transition replay of a full debug pytest run.

Relationship to other work

First steps

  1. Historical bug corpus: tag past control-flow/resource fixes with their FSM (grounds the risk ranking).
  2. Pilot A (docs/fsm/conn.md) + the trace checker.
  3. Pilot B (docs/fsm/port-messaging.md), then the TLA+ model transcribed from its verified YAML.

Findings are expected from the first doc onward — writing the transition table is the audit.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions