# RFPSimpleStrategy milestones can be set multiple times

RFPSimpleStrategy.setMilestones guards on upcomingMilestone==0, which stays 0 until first distribution, so milestones can be appended repeatedly, exceeding 100% totalAmountPercentage.

- Payout: Contest reward
- Severity: medium
- Program: Gitcoin
- Credited: GimelSec
- Disclosed: 2023-09-01
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-09-Gitcoin-judging/issues/176
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-09-Gitcoin%3AM-3
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** RFPSimpleStrategy.sol setMilestones (Allo v2 strategy contract)
**Root cause:** The already-set check uses upcomingMilestone != 0, but upcomingMilestone only increments after a distribution, so it remains 0 and the guard never trips before first payout.
**Steps:**
1. Deploy/obtain an RFPSimpleStrategy pool with no distribution yet (upcomingMilestone == 0).
2. Call `setMilestones([...])` with milestones summing to <=100% totalAmountPercentage.
3. Call `setMilestones([...])` again with additional milestones; they are appended, not replaced.
4. Observe combined totalAmountPercentage > 100% and duplicated milestones that cannot be reset.
**How found:** Code reading of the setMilestones guard: noticed the check keys off upcomingMilestone, which is only advanced by distribution, so the precondition is trivially satisfiable pre-distribution.
**Exploitation:** Manager (or colluding manager+recipient) repeatedly calls setMilestones before first distribution, stacking milestones so cumulative totalAmountPercentage exceeds 100%, inflating payout beyond intended pool allocation; alternatively accidental duplicate calls permanently corrupt pool state requiring recreation.
**Insight:** When a 'already initialized' guard keys off a counter that only advances on a later lifecycle event, verify the counter's initial value and every path that mutates it; state-set flags should be checked against the actual stored state (e.g. milestones.length) rather than a progress index.
