OpenZeppelin's AccessControl allows any role holder to call renounceRole(bytes32 role, address account) where account must be msg.sender. When a signer calls this function, they lose the SIGNING_ROLE but remain in the s_signers[] array with s_isSigner[signer] = true and are still counted in s_signerCount. This creates "gost signers" who:
Cannot confirm transactions (no role)
Still count toward MAX_SIGNER_COUNT
Block addition of new functional signers
Are not removed from s_signers[] array
Likelihood:
The renounceRole() function is a standard function from the inherited library, therefore it could be called intentionally or unintentionally.
Impact:
Only the OpenZeppelin implementation of a role is renounced, leaving all state variables unchanged; the signer itself remains active but useless.
New signers cannot be added when compromised signers exist.
Reduces effective signer count (5→4→3).
At 3 signers, becomes 3-of-3 (no redundancy, single point of failure).
Ghost signers create confusion and management overhead.
Inconsistent state between AccessControl and multisig tracking
Add this snipped of code to the MultiSigTimelockTest.t.sol test file.
How to execute:
Override `renounceRole` to properly delete signer from the protocol.
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.
The contest is complete and the rewards are being distributed.