Normal behavior: expiry is the pool's deadline: after it, the permissionless claimExpired backstop lets stakers recover principal, and the fabricated-/real-CORRUPTED freeze resolves at expiry + MODERATOR_CORRUPTED_GRACE. The design locks expiry at the first stake (expiryLocked) with the stated intent, per DESIGN.md, of protecting staker reliance — a staker should be able to trust the expiry they see when they deposit.
The issue: That protection is incomplete in two compounding ways. First, expiry has no sane upper bound: setExpiry/createPool only reject values above type(uint32).max (~year 2106), so the sponsor can set the deadline ~80 years out. Second, stake accepts only amount — it has no "expected/maximum expiry" parameter and only checks that expiry has not already elapsed — so a staker's transaction commits to whatever expiry is in storage at execution time. Because expiry is mutable right up until the first stake latches expiryLocked, a sponsor can watch the mempool and front-run the first stake with setExpiry(type(uint32).max); the victim's stake then lands and permanently locks the extreme value, with no lever the staker could have used to reject it. This stretches the maximum no-exit / no-claim freeze window from months to decades and amplifies every sponsor-driven freeze path (fabricated CORRUPTED, goToProduction premium denial).
Likelihood:
The pool owner is the sponsor and holds setExpiry; the value is mutable up to the exact moment the first stake latches expiryLocked, so the front-run window is open on every fresh pool.
A staker's stake(amount) transaction carries no expiry expectation, so when the sponsor front-runs setExpiry, the victim's transaction cannot revert on the changed value — it silently locks the extreme expiry.
Even absent a front-run, a malicious sponsor sets an unreasonable expiry at createPool; stakers who do not scrutinize the raw value inherit it.
Impact:
The maximum window during which a staker has no exit and no claim — expiry + MODERATOR_CORRUPTED_GRACE — is stretched from months (e.g. 211 days at a 31-day expiry) to 80 years.
Under any sponsor-driven freeze (fabricated CORRUPTED, or simply an agreement that never resolves), the staker cannot withdraw (risk window sealed), cannot claimSurvived (no flag), and cannot claimExpired until the locked expiry passes. Their capital is frozen at zero yield for the full stretched window.
It is a force-multiplier on the fabricated-CORRUPTED principal-sweep finding: the same attack, with a front-run expiry, holds stakers hostage for decades before the sweep, rather than ~6 months.
Principal is ultimately returned, so this is capital lock / opportunity-cost griefing, not theft — hence Low severity despite the extreme duration.
Verified, passing (mock-based; runs with a plain forge test).
Add a sane upper-lead bound in both setExpiry and createPool/initialize, so the worst-case freeze is proportionate to a testnet agreement's real lifecycle:
To eliminate the front-run entirely (not just bound its damage), also let the staker commit to a ceiling:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.