rustfund::lib::contribute, rustfund::lib::refund · Confidence: 75 (capped from 78) · Severity: Medium
Validation summary
| Stage | Verdict | Note |
|---|---|---|
| 2 — Audit | candidate at Medium, conf 90 | Execution Trace, Invariant, Periphery, Vector Scan (V34) |
| 3 — PoC | ADVANCE | Tier 2 (TS test demonstrating two contributes succeed while deadline=0) |
| 4 — Adversarial Pass D | calibrated Medium (CONFIRMED) | Challenge 2 (refund-side currently inert via F-02) bounds but does not invalidate |
| 5 — Platform | ADVANCE at Medium, score 78/100 | aligns with CodeHawks "Medium" (indirect impact, specific condition) |
| 6 — Program | in-scope | same scope chain |
| 7 — Duplication | no hard duplicates; risk of judge-side merge with F-03 noted |
Root cause
Both deadline guards (programs/rustfund/src/lib.rs:29 in contribute and lib.rs:69 in refund) use if fund.deadline != 0 && <time-comparison> — treating 0 as a sentinel for "no deadline." Since fund_create initializes fund.deadline = 0, the post-init pre-set_deadline window is exactly the state in which both guards short-circuit to false.
Location
rustfund::lib::contribute — programs/rustfund/src/lib.rs:29
rustfund::lib::refund — programs/rustfund/src/lib.rs:69
Exploit path
Creator calls fund_create(...) — fund.deadline = 0.
Creator delays calling set_deadline (or never calls it).
Any contributor calls contribute(amount) — guard at lib.rs:29 evaluates fund.deadline != 0 to false, short-circuits the &&, and skips the deadline check. Contribution accepted.
Repeat indefinitely — fund.amount_raised grows without bound while deadline remains 0.
(Refund branch — currently inert because of F-02; live once F-02 is fixed): contributor calls refund — guard at lib.rs:69 short-circuits the same way; refund executes regardless of whether any deadline has been reached.
Proof of concept
tier: 2 (TS test)
file: argus/20260504T004143Z/3-poc/F-04/poc.ts
reproduction: argus/20260504T004143Z/3-poc/F-04/repro.md
proven impact: With deadline=0, two successive contribute calls succeed; fund.amount_raised grows by 0.3 SOL across the calls without the deadline guard ever firing.
Same as F-01.
Copy argus/20260504T004143Z/3-poc/F-03/poc.ts to tests/poc-F-03.ts.
Run anchor test.
Observe the it block PASSES — proving the deadline can be re-set repeatedly.
Add fund.dealine_set = true; after the fund.deadline = deadline; assignment in set_deadline (programs/rustfund/src/lib.rs:61):
After the fix, the second call to setDeadline errors with DeadlineAlreadySet and the test FAILS at the second setDeadline call.
Impact
Indirect fund impact: amplifies F-01's drainage pool by accepting contributions indefinitely; post-F-02-fix, also enables refund without any deadline-reached precondition (contradicting README's "if deadlines are reached and goals aren't met"). Maps to CodeHawks "Medium": "Indirect impact on the funds or the protocol's functionality. The attack path isn't straightforward and needs specific conditions."
Recommendation
Replace the short-circuit checks with explicit dealine_set validation (chain with the F-03 fix that flips dealine_set = true):
Add DeadlineNotSet to ErrorCode. Note: this fix only works once F-03's fix is applied (so dealine_set reliably becomes true after set_deadline).
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.