A zero that matched another zero
One stale field let any account claim admin over 82 markers on Provenance. The check compared a supply that was never written back.
SeverityHigh
3 minSmart Contract Audits
Trail of Bits published a finding on 25 August in Provenance Blockchain, a proof-of-stake chain on the Cosmos SDK, in the marker module that manages fungible tokens. The bug is a state divergence: two places hold what is supposed to be the same fact, and only one of them is kept current.
How it worked
The authorisation check in accountControlsAllSupply asked whether the caller holds the entire supply of a marker. It read the supply from the marker's own stored field. For non-fixed-supply markers that field is never written back after minting, because the live circulating count lives in the bank module, so it stayed at zero.
A new account also holds zero. The check compared zero against zero, found them equal, and concluded the caller controlled all supply. Two transactions did the rest: one to add admin permissions, one to mint or withdraw.
The exposure
82 active markers were vulnerable, among them the ones behind Provenance Foundation grant programmes and validator incentive funds. The most direct risk named is roughly $500,000 in nhash held in escrow across three markers.
The fix, and what it teaches
PR #2734 changed one line: read k.bankKeeper.GetSupply() instead of the marker's internal field. That is the whole patch.
Bugs of this shape do not look like bugs in review. Every individual piece is correct: the marker has a supply field, the bank module has the real supply, the authorisation check compares two numbers. The defect lives in the assumption that the cached copy is maintained, and an assumption is exactly the thing a line-by-line read does not test. The question worth carrying to the next review is not whether a comparison is correct but whether both sides of it are alive.
Retold from Trail of Bits. This is a summary in our own words; follow the link for the original reporting.