Skip to content

feat(kms/auth-eth): formal verification — Slither + Halmos + spec#689

Open
h4x3rotab wants to merge 2 commits into
foundryfrom
feat/auth-eth-formal-verification
Open

feat(kms/auth-eth): formal verification — Slither + Halmos + spec#689
h4x3rotab wants to merge 2 commits into
foundryfrom
feat/auth-eth-formal-verification

Conversation

@h4x3rotab
Copy link
Copy Markdown
Contributor

@h4x3rotab h4x3rotab commented May 19, 2026

Layers static analysis + a focused symbolic-execution suite on top of #252. Depends on #252 merging first — base is the foundry branch.

What's verified

  • Slither static analysis with project-tuned suppressions in slither.config.json. Baseline: 0 findings.
  • 15 Halmos symbolic properties (5 in DstackApp.symbolic.t.sol, 10 in DstackKms.symbolic.t.sol). Each proves something a unit test or bounded fuzz couldn't. Highlights:
    • check_UpgradesDisabled_StepPreservation — INV-1 inductive step: from the disabled state, no single call to any enumerated mutating function (symbolic selector + args + caller, issued against the proxy) flips _upgradesDisabled back to false. Validated by mutation testing — a permissionless flag-reset and an owner-callable flag-reset are both caught.
    • check_IsAppAllowed_DelegatesFaithfully + check_IsAppAllowed_PropagatesMockRevert — the two delegation paths (faithful return; revert propagation) for a registered IAppAuth
    • check_IsAppAllowed_Rejects{UnregisteredApp,UnknownOsImage} + check_IsKmsAllowed_Rejects{UnknownMr,UnknownDevice} — fully symbolic AppBootInfo, gates short-circuit correctly
    • check_DeployAndRegisterApp_PostState / _DefaultsTcbToFalse — factory post-state including the conditional initial-state branches
    • check_Initialize_OnceOnly — INV-3 (proxy initialized exactly once)
  • docs/specification.md — normative pre/post/frame/events/reverts, state invariants with honest verification status, trust model, adversarial scenarios, four open product questions.
  • docs/formal-verification.md — roadmap, test inventory, and an explicit list of what we do not verify (with reasons).

Honesty notes (what this is NOT)

  • INV-1 is not a complete cross-transaction proof. It has a verified inductive step (from the canonical disabled state) plus a source-inspection closure (the slot has exactly two writers; the initializer path is closed by check_Initialize_OnceOnly). Quantifying the step over arbitrary disabled pre-states needs symbolic storage, which Halmos 0.3.3 lacks — documented as a residual gap. An earlier invariant-mode attempt was discarded because it passed vacuously (the auto-target fuzzer drove the implementation while the assertion read the proxy); the current step-test reads and writes the same proxy storage and is mutation-tested.
  • Per-function _OnlyOwner tests omitted — fuzz-tests in symbolic clothing against OZ's onlyOwner.
  • TCB byte-exactness left to the spec (circular under Halmos's uninterpreted-keccak).
  • Universal quantification over registered contracts, INV-2/INV-6, frame conditions — documented gaps.

Findings

DstackKms.registerApp is permissionless by design (confirmed, in natspec). The delegation tests prove an attacker who registers their own contract is still gated by the owner-controlled allowedOsImages whitelist, and that their contract's revert propagates rather than being silently mapped to a rejection.

Open product questions

  1. Should renounceOwnership be overridden to revert?
  2. Should KMS track which appImplementation was current per app at deploy time?
  3. Should removeOsImageHash retroactively un-authorize already-running apps?
  4. Should gatewayAppId validate format?

How to verify

pipx install slither-analyzer halmos
cd kms/auth-eth
slither .                                  # 0 findings
halmos --contract DstackAppSymbolicTest    # 5 / 5
halmos --contract DstackKmsSymbolicTest    # 10 / 10
forge test --ffi                           # 46 / 46

🤖 Generated with Claude Code

@h4x3rotab h4x3rotab mentioned this pull request May 19, 2026
@h4x3rotab h4x3rotab force-pushed the feat/auth-eth-formal-verification branch 2 times, most recently from ff9845b to c1cd27b Compare May 28, 2026 12:09
h4x3rotab and others added 2 commits May 30, 2026 03:39
Layers a verification stack on top of the Hardhat→Foundry migration:

- Slither static analysis with project-tuned suppressions
  (kms/auth-eth/slither.config.json): excludes naming-convention and
  solc-version detectors that hit OZ-idiom noise across our codebase,
  plus four per-line justified `// slither-disable-next-line` comments
  in contracts/DstackKms.sol (factory reentrancy-benign on
  deployAndRegisterApp's `new ERC1967Proxy`, named-return-forward
  unused-return on the IAppAuth delegation, two unindexed-event-address
  for backward compatibility with deployed log indexers). Baseline:
  0 findings.

- Halmos symbolic execution: 28 properties across
  test/{DstackApp,DstackKms}.symbolic.t.sol, each `check_*` function
  treats its arguments as symbolic. Owner-only mutations, isAppAllowed
  decision tables (including the byte-exact TCB compare), disableUpgrades
  monotonicity, factory atomicity + TCB flag propagation, both 5- and
  6-arg initializer overloads. All pass.

- docs/formal-verification.md is the layered roadmap (Slither → Halmos
  → optional Certora). SMTChecker (Layer 1) is deferred — forge's solc
  binaries lack z3 linkage and BMC alone gives zero useful output on
  OZ-upgradeable patterns. Halmos subsumes its coverage for our
  contracts.

Both tools run locally (pipx install). Intentionally not in CI —
symbolic execution is slow, solver-version-sensitive, and most valuable
as an interactive design check rather than a PR gate.

Findings worth review:
- DstackKms.registerApp is permissionless by design (no access control,
  only require !=0). Confirmed-intentional; the downstream
  allowedOsImages whitelist + delegated isAppAllowed gate authorization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Normative pre/post/frame conditions, state invariants, cross-contract
assumptions, and adversarial scenarios for DstackKms + DstackApp,
independent of code. Each property cites the Halmos test that verifies
it; properties without a citation are explicit gaps.

Sections:
- Trust model (principals, environment, off-chain pipeline)
- Storage layout per contract with frame-condition rules
- Per-function pre/post/frame/events/reverts specs for the entire
  public surface
- Decision-table semantics for isAppAllowed
- State invariants INV-1 through INV-7 with verification status
- Adversarial scenarios: malicious registerApp + downstream gates,
  reentrancy structural-impossibility argument, gas-griefing,
  front-running, initializer selector collision
- Known verification gaps (cross-transaction invariants,
  universal-quantification over registered apps, KEVM bytecode-level)
- Open product-level questions for the team:
  1. Should renounceOwnership be overridden to revert?
  2. Should KMS track which appImplementation was current per app?
  3. Should removeOsImageHash retroactively un-authorize?
  4. Should gatewayAppId validate format?

Deliverable for any future external audit-firm engagement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@h4x3rotab h4x3rotab force-pushed the feat/auth-eth-formal-verification branch from c1cd27b to a271cd6 Compare May 30, 2026 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant