31355 - [SC - Low] Past Defeated Proposals Can Be Executed in the ...

Submitted on May 17th 2024 at 16:06:05 UTC by @Breeje for Boost | Alchemix

Report ID: #31355

Report type: Smart Contract

Report severity: Low

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/AlchemixGovernor.sol

Impacts:

  • Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results

Description

Brief/Intro

The AlchemixGovernor contract uses an implementation of L2GovernorVotesQuorumFraction, which has a known vulnerability. If a proposal passes to lower the quorum requirement, all past proposals that were defeated solely due to a lack of quorum may become executable if the votes they received now meet the new, lower quorum requirement.

Vulnerability Details

The AlchemixGovernor contract inherits from L2GovernorVotesQuorumFraction, whose implementation is similar to OpenZeppelin's GovernorVotesQuorumFraction contract version 4.5.0.

Proposal Execution Process

To understand the issue, we need to examine how a proposal is executed:

  1. The execute function identifies the state of the proposal by calling the state function.

  1. The state function, after validation, checks two things: whether quorum was reached and whether the vote succeeded.

  1. The _quorumReached function calls the quorum function with proposalSnapshot(proposalId) as the timestamp.

  1. The quorum function of L2GovernorVotesQuorumFraction is implemented as shown below:

Notice the following critical point:

  • While the function gets the total supply value based on the timestamp, it uses the quorumNumerator() function, which returns the current quorumNumerator value rather than the value at the time of blockTimestamp.

Consequences of the Issue

  1. If quorum was not reached for a past proposal, it cannot be executed at that time and is in a defeated state.

  2. If governance updates the _quorumNumerator to a lower value using updateQuorumNumerator function:

    • The past defeated proposal may now reach the quorum with the new lower requirement.

    • The proposal state can change to succeeded, making it executable again.

Impact Details

Past proposals that were defeated only due to a lack of quorum may become executable if the number of votes they received meets the new quorum requirement.

References and Mitigation

This vulnerability is recognized by OpenZeppelin with a high severity rating. They issued a security advisory for it, which can be found here.

OpenZeppelin mitigated this issue in version 4.7.2 by:

  • Deprecating the direct use of _quorumNumerator in the quorumNumerator() function.

  • Implementing Checkpoints for the new _quorumNumeratorHistory variable to track past values of the quorum numerator relative to time and using the value as it was at the blockTimestamp.

The changes are detailed in this commit.

Proof of Concept

Adding Test

Add the following lines in the L2Governor contract:

Add the following testPoC function in AlchemixGovernorTest.t.sol test:

Running Test

Use the following command to run the test:

Expected Test Result

Analysis of the Result

In the test logs, you can see the following key points:

  1. Initial Voting Power vs Quorum:

    • Initial Voting Power: 63473899543378978660812

    • Initial Quorum: 92037551049771679068955

    • Can clearly see Voting Power < Quorum.

  2. Voting on Proposal:

    • Proposal created and voted on.

    • Initial attempt to execute fails due to not meeting quorum.

  3. Updating Quorum:

    • Quorum numerator is updated from 20% to 11%.

  4. Final Voting Power vs New Quorum:

    • Final Voting Power: 62597183155758481682946

    • Final Quorum: 49921468744534494597083

    • Can clearly see, Voting Power > Quorum now for same past proposal.

  5. Executing Past Proposal:

    • The past proposal, initially defeated due to lack of quorum, now meets the new quorum and is executed successfully.

This demonstrates the vulnerability where reducing the quorum can make previously defeated proposals executable, posing a significant governance risk.

Last updated

Was this helpful?