feat(kms/auth-eth): formal verification — Slither + Halmos + spec#689
Open
h4x3rotab wants to merge 2 commits into
Open
feat(kms/auth-eth): formal verification — Slither + Halmos + spec#689h4x3rotab wants to merge 2 commits into
h4x3rotab wants to merge 2 commits into
Conversation
42f518b to
ff9845b
Compare
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>
ff9845b to
c1cd27b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Layers static analysis + a focused symbolic-execution suite on top of #252. Depends on #252 merging first — base is the
foundrybranch.What's verified
slither.config.json. Baseline: 0 findings.test/{DstackApp,DstackKms}.symbolic.t.sol. Eachcheck_*proves a property a unit test or bounded fuzz couldn't. Highlights:check_IsAppAllowed_DelegatesFaithfully(benign-shape registered contract) +check_IsAppAllowed_PropagatesMockRevert(reverting registered contract) — the two delegation paths the spec requirescheck_IsAppAllowed_Rejects{UnregisteredApp,UnknownOsImage}andcheck_IsKmsAllowed_Rejects{UnknownMr,UnknownDevice}— fully symbolicAppBootInfo, gates short-circuit correctlycheck_DeployAndRegisterApp_PostStateand_DefaultsTcbToFalse— factory post-state holds for any input combination including the conditional initial-state branchescheck_Initialize_OnceOnly— verifies INV-3 (proxy initialized exactly once); both overloads revert for any input after setUpdisableUpgradesand the initializer-defaultstest/DstackAppInvariants.t.sol:invariant_UpgradesDisabled_Monotonic— runs underhalmos --invariant-depth 2, exhaustively explores all 2-call symbolic sequences across the contract's external surface from the disabled state. Verifies INV-1 (_upgradesDisabledmonotonicity). Also passes at depth 3 (1024 paths, ~20s).docs/specification.md— normative pre/post/frame/events/reverts for the full public surface, state invariants with verification status (INV-1 and INV-3 verified, others gap), trust model, adversarial scenarios, four open product-level questions for the team.docs/formal-verification.md— the layered FV roadmap, the test inventory, and an explicit list of what we deliberately do not verify (with reasons).What's intentionally out of scope (and why)
_OnlyOwnertests — fuzz-tests in symbolic clothing against OZ's well-testedonlyOwnermodifierIAppAuthcontract — Halmos instantiates one mock at a time; we bound the relevant shapes (faithful return; revert propagation) and the spec is explicit about treating the registered contract's output as untrusted downstream__gapzeroes) — gaps; would mirror the shape of the INV-1 invariant testOpen product-level questions
The specification ends with four decisions the team should pin down:
renounceOwnershipbe overridden to revert?appImplementationwas current per app at deploy time?removeOsImageHashretroactively un-authorize already-running apps?gatewayAppIdvalidate format?Findings
DstackKms.registerAppis permissionless by design (confirmed-intentional, now in natspec). The twocheck_IsAppAllowed_*mock tests prove that an attacker who registers their own contract is still gated by the owner-controlledallowedOsImageswhitelist and their contract's revert propagates rather than being silently mapped to a rejection.How to verify
🤖 Generated with Claude Code