This report analyzes a vulnerability in the getCurrentPower
function, where unsafe typecasting is employed during voting power calculations. The vulnerability primarily arises from a dual-type mismatch and lack of proper overflow checks, which can lead to critical miscalculations and potential exploits in governance mechanisms.
Dual-Type Mismatch
Signed vs. Unsigned: The point.bias
variable is declared as an int128
(signed), yet voting power inherently must be non-negative.
Unsafe Casting: Casting from int128
to uint128
(and subsequently to uint256
) can lead to improper sign handling. For instance, a negative bias
value, due to incorrect calculations, would convert to a large positive number when cast, thereby inflating a user’s voting power erroneously.
Calculation Risks
Overflow During Initialization: In calculateAndUpdatePower
, the bias
is derived from an initialPower
(a uint256
) via:
If initialPower
exceeds 2^127 - 1
, the downcast overflows, causing bias
to become negative.
Incorrect Result Example: For instance, an initialPower
of 2^127
results in bias
being set to -2^127
. When subsequently cast to uint128
, this negative value converts to 2^127
, drastically distorting the voting power calculation.
Missing Safety Checks
Validation Gaps: There is no validation to ensure that bias
remains non-negative or fits within the uint128
range.
Silent Truncation/Overflow: The lack of explicit checks can cause silent overflow or truncation, corrupting the intended voting power values without triggering any errors.
Incorrect Voting Power: Users might experience massively inflated or erroneously reduced voting power. Negative bias
values, when misinterpreted as large positive numbers, can lead to incorrect token voting weights.
Governance Attacks: Malicious actors may exploit these miscalculations to manipulate voting outcomes, undermining the protocol’s integrity.
Fund Locking: The miscalculation may result in tokens being permanently locked or misallocated, disrupting the overall tokenomics and governance structure.
Use uint128
for Bias
Redesign the RAACVoting.Point
structure to store bias
as uint128
since voting power should always be non-negative:
Update all related calculations to consistently use uint128
.
Add Overflow Checks
Incorporate explicit validations when setting bias
:
This ensures that values do not exceed the permissible range and prevents silent overflow.
Adjust Return Logic
With bias
stored as uint128
, the return logic in getCurrentPower
can be simplified:
This eliminates the need for unsafe casts and guarantees correct interpretation of the voting power.
Fix calculateInitialPower
Remove redundant casts once the bias
type is updated:
Consider a scenario where a user locks tokens resulting in:
Initial Condition: initialPower = 2^128
Current Code Behavior:
bias = int128(2^128)
overflows, resulting in bias = -2^127
.
When cast via uint256(uint128(point.bias))
, the negative value -2^127
becomes 2^127
, erroneously granting an excessively high voting power.
Fixed Code Behavior:
The check require(2^128 <= type(uint128).max)
would fail, reverting the transaction and preventing invalid locks.
Alternatively, if properly bounded, the new design with uint128
would correctly handle the calculation without misinterpretation.
The unsafe typecast in the getCurrentPower
function, combined with the use of a signed type for inherently unsigned voting power calculations, introduces a significant vulnerability. This mismanagement of data types can lead to either inflated or deflated voting power, posing risks of governance attacks and token misallocation. Addressing this issue requires restructuring the data types, adding robust overflow checks, and revising related calculations to ensure the integrity of the voting mechanism. Implementing these changes is critical to maintaining protocol security and preventing potential exploits.
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.