# arbitraryCall() can get blocked by an attacker

Attacker sends a token as incentive to block arbitraryCall()'s incentives[who]==0 check, preventing gov from claiming airdrops before stream end.

- Payout: Audit contest
- Severity: medium
- Program: Streaming Protocol
- Found by: GiveMeTestEther
- Disclosed: 2022-02-11
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-11-streaming-findings/issues/47
- Library page: https://bugbountylibrary.com/r/c4%3A2021-11-streaming%3AM-04
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** Locke.sol arbitraryCall() / createIncentive() incentive accounting
**Root cause:** arbitraryCall() requires incentives[who]==0, but anyone can set incentives[who] via createIncentive(); resetting requires claimIncentive() only callable by stream creator after endStream, so a griefing token deposit blocks the call.
**Steps:**
1. Attacker calls `createIncentive()` sending a token `who` that gov intends to use in `arbitraryCall()`
2. `incentives[who]` becomes nonzero
3. gov calls `arbitraryCall()` targeting `who`; L735 `require(incentives[who] == 0, "inc")` reverts
4. Stream creator can only zero it via `claimIncentive()` after `block.timestamp >= endStream` (L520)
5. If airdrop claimable only before stream end, claim is permanently blocked; even if reset, attacker can re-send between non-atomic reset and arbitraryCall
**How found:** Code reading of arbitraryCall()'s require(incentives[who]==0) check combined with observing createIncentive() is permissionless and claimIncentive() is time-gated to stream end.
**Exploitation:** Griefing: attacker spends a token deposit via createIncentive() to make incentives[who] nonzero, causing arbitraryCall() to revert and denying gov the ability to claim an airdrop for that token before stream end.
**Insight:** When a privileged function guards on a mapping value being zero, check whether any permissionless path can set that value; time-gated or creator-only resets create a griefing window, especially if reset and use aren't atomic.
