Deletion does not delete all values
Description
In VotingControllerStorageUpg, _removePool deletes poolData, but the slopeChanges mapping within poolData is not actually deleted. Currently, since poolData is always checked for activity when used, this does not pose a security risk.
Impact
Using delete on a (possibly nested) struct containing a mapping member does not delete the mapping. The following proof-of-concept script demonstrates that the slopeChanges mapping within poolData is not actually deleted.
contract AuditTest is Test {
address public admin;
address public user1;
// for locking token
ProxyAdmin public proxyAdminLockingToken;
TransparentUpgradeableProxy public proxyLockingToken;
LockingToken public lpLocking;
MockERC20 public lockedToken;
// for voting controller
ProxyAdmin public proxyAdminAnzenVotingControllerUpg;
TransparentUpgradeableProxy public proxyAnzenVotingControllerUpg;
AnzenVotingControllerUpg public anzenVotingControllerUpg;
MockERC20 public anzenToken;
function setUp() public {
admin = vm.addr(0x1);
user1 = vm.addr(0x2);
setUp_LockingToken();
setUp_VotingController();
}
function setUp_LockingToken() internal {
vm.startPrank(admin);
lockedToken = new MockERC20("Locked Token", "LCK");
proxyAdminLockingToken = new ProxyAdmin();
proxyLockingToken = new TransparentUpgradeableProxy(address(new LockingToken()), address(proxyAdminLockingToken), "");
lpLocking = LockingToken(address(proxyLockingToken));
lpLocking.initialize(
"Anzen LP Locking",
"LockLP",
address(lockedToken),
100000000,
address(0),
address(0)
);
lockedToken.mint(user1, 100 ether);
vm.stopPrank();
}
function setUp_VotingController() internal {
vm.startPrank(admin);
anzenToken = new MockERC20("Anzen Token", "ANZ");
proxyAdminAnzenVotingControllerUpg = new ProxyAdmin();
proxyAnzenVotingControllerUpg = new TransparentUpgradeableProxy(address(new AnzenVotingControllerUpg(address(anzenToken), address(0x1337), 0)), address(proxyAdminAnzenVotingControllerUpg), "");
anzenVotingControllerUpg = AnzenVotingControllerUpg(address(proxyAnzenVotingControllerUpg));
anzenVotingControllerUpg.initialize();
vm.stopPrank();
}
function testAuditRemovePoolDirty() public {
vm.startPrank(admin);
// write to PoolData storage slot
address pool = address(0x1337);
anzenVotingControllerUpg.addPool(
0,
pool,
100,
100
);
address[] memory pools = new address[](1);
uint64[] memory weights = new uint64[](1);
pools[0] = pool;
weights[0] = 100;
anzenVotingControllerUpg.vote(pools, weights);
anzenVotingControllerUpg.removePool(address(0x1337));
// check slopeChanges mapping slot dirty
uint128[] memory wTimes = new uint128[](1);
wTimes[0] = 62899200;
(,,,uint128[] memory dirty) = anzenVotingControllerUpg.getPoolData(address(0x1337), wTimes);
console.log("pool slopeChanges slot dirty: ", dirty[0]);
vm.stopPrank();
}
}The following text is the result of the proof-of-concept script:
[PASS] testAuditRemovePoolDirty() (gas: 313463)
Logs:
pool slopeChanges slot dirty: 15Recommendations
Consider adding an array to record the keys of the nested map slopeChanges, and iterate over the array to delete all values in the nested map when performing deletion.
Remediation
Anzen Labs Inc. provided the following response:
The team decided to not make changes for those issues at this time