The impact of this vulnerability, i.e., losing all governance control is very High.
There is a possibility, due to a corner case as described below.
Proof of Concept
Contract : RoleManager.sol
Function : renounceGovernance()
Step 0:
Let current governance role given to = CURRENT_GOV_ADDRESS
so, getRoleMemberCount() for "governance" role will return = 1
Step 1: Add a new address say ALICE to governance role, by addGovernor(ALICE)
now, ALICE also has governace role, and getRoleMemberCount() for "governance" role will return = 2
Step 2: Assume that ALICE renounces governance role, by renounceGovernance()
now, ALICE does not have governance role, but getRoleMemberCount() for "governance" role will return = 2, due to a BUG
Step 3: In some distant future, if there is a compromise of CURRENT_GOV_ADDRESS keys or due to some other reason,
its decided to revoke governance role for CURRENT_GOV_ADDRESS via renounceGovernance(), and the command succeeds
It can be assumed that since getRoleMemberCount() for "governance" role returns = 2, at least there is one other active governor address.
But now, CURRENT_GOV_ADDRESS does not have governance role, and the total governance control on the protocol is lost my mistake.
Recommended Mitigation Steps
getRoleMemberCount() currently returns _roleMembers[role].length();
It should return the count only for _roles[role].members[account] = true;
Its recommended to add a new function to know who are the active members for any role,
like getRoleMembers(bytes32 role) returning address account []
Lines of code
https://github.com/code-423n4/2022-04-backd/blob/c856714a50437cb33240a5964b63687c9876275b/backd/contracts/access/RoleManager.sol#L43-L46 https://github.com/code-423n4/2022-04-backd/blob/c856714a50437cb33240a5964b63687c9876275b/backd/contracts/access/RoleManager.sol#L115-L128
Vulnerability details
Impact
The impact of this vulnerability, i.e., losing all governance control is very High. There is a possibility, due to a corner case as described below.
Proof of Concept
Contract : RoleManager.sol Function : renounceGovernance()
Recommended Mitigation Steps
getRoleMemberCount() currently returns _roleMembers[role].length(); It should return the count only for _roles[role].members[account] = true;
Its recommended to add a new function to know who are the active members for any role, like getRoleMembers(bytes32 role) returning address account []