安全审计中 复杂Storage的结构体删除常见错误

变量 accountRoles 占用的 slot = 0, slot0里面存储的值也是0. 因此删除 delete accountRoles 并不会删除整个的storage(即把整个的storage置0)


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title RoleAccessControl

/// @dev Library for managing role-based access control.

contract ComplexStorage {

using EnumerableSet for EnumerableSet.Bytes32Set;

bytes32 constant public ROLE_ADMIN = "ADMIN";

bytes32 constant public ROLE_UPGRADE = "UPGRADE";

bytes32 constant public ROLE_CONFIG = "CONFIG";

bytes32 constant public ROLE_KEEPER = "KEEPER";

/// @dev Error thrown when an account does not have the required role.

error InvalidRoleAccess(address account, bytes32 role);

/// @dev Error thrown when an invalid role name is provided.

error InvalidRoleName(bytes32 role);

mapping(address => EnumerableSet.Bytes32Set) accountRoles;

modifier onlyRoleAdmin() {

if (!hasRole(msg.sender, ROLE_ADMIN)) {

revert InvalidRoleAccess(msg.sender, ROLE_ADMIN);

}

_;

}

constructor() {

grantRole(msg.sender, ROLE_ADMIN);

}

/// @dev Checks if the caller has the specified role.

/// @param role The role to check.

function checkRole(bytes32 role) public view {

if (!hasRole(msg.sender, role)) {

revert InvalidRoleAccess(msg.sender, role);

}

}

/// @dev Checks if the caller has the specified role.

/// @param role The role to check.

/// @return True if the caller has the role, false otherwise.

function hasRole(bytes32 role) public view returns (bool) {

return hasRole(msg.sender, role);

}

/// @dev Checks if an account has the specified role.

/// @param account The account to check.

/// @param role The role to check.

/// @return True if the account has the role, false otherwise.

function hasRole(address account, bytes32 role) public view returns (bool) {

return accountRoles[account].contains(role);

}

/// @dev Grants a role to an account. public for test

/// @param account The account to grant the role to.

/// @param role The role to grant.

function grantRole(address account, bytes32 role) public {

accountRoles[account].add(role);

}

/// @dev Revokes a role from an account. public for test

/// @param account The account to revoke the role from.

/// @param role The role to revoke.

function revokeRole(address account, bytes32 role) public {

if (accountRoles[account].contains(role)) {

accountRoles[account].remove(role);

}

}

/// @dev Revokes all roles from an account. public for test

/// @param account The account to revoke all roles from.

function revokeAllRole(address account) public {

delete accountRoles[account];//@audit 问题在这里,这个slot的值本身就是0,比如是slot111222,它只起到计算后面的EnumerableSet.Bytes32Set的slot的作用

}

}

以上面的代码为例: 变量 accountRoles 占用的 slot = 0, slot0里面存储的值也是0. 因此删除 delete accountRoles 并不会删除整个的storage(即把整个的storage置0)

同理: accountRoles[account] 占用的slot = keccak256(abi.encode(account, 0)) 假设为slotX, slotX里面存储的值也是0. 因此删除 accountRoles[account] 也不会删除 里面 EnumerableSet.Bytes32Set结构体的值。它只是 让 slotX这个slot里面存储值=0,而已。而这个值本身就是0. slotX的作用是让结构体 来寻址用的 keccak256(abi.encode(Value, slotX)),Value为account或者动态数组的下标如1,2,3

测试代码:

// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {Test, console2} from "forge-std/Test.sol";

import {ComplexStorage} from "../../src/mapping-storage/ComplexStorage.sol";

contract ComplexStorageTest is Test{

bytes32 constant public ROLE_ADMIN = "ADMIN";

bytes32 constant public ROLE_UPGRADE = "UPGRADE";

bytes32 constant public ROLE_CONFIG = "CONFIG";

bytes32 constant public ROLE_KEEPER = "KEEPER";

address public user1;

ComplexStorage public cs;

function setUp() public {

cs = new ComplexStorage();

user1 = makeAddr("user1");

}

function testHasAdminRole() public{

assertEq(cs.hasRole(ROLE_ADMIN), true);

assertEq(cs.hasRole(ROLE_UPGRADE), false);

}

function testAddAndRemoveRole() public{

cs.grantRole(user1, ROLE_UPGRADE);

cs.grantRole(user1, ROLE_CONFIG);

cs.grantRole(user1, ROLE_KEEPER);

assertEq(true, cs.hasRole(user1, ROLE_UPGRADE));

assertEq(false, cs.hasRole(user1, ROLE_ADMIN));

cs.revokeRole(user1, ROLE_UPGRADE);

//remove role_upgrade

assertEq(true, cs.hasRole(user1, ROLE_KEEPER));

assertEq(false, cs.hasRole(user1, ROLE_UPGRADE));

cs.revokeAllRole(user1);

//nothing change

assertEq(true, cs.hasRole(user1, ROLE_KEEPER));

assertEq(true, cs.hasRole(user1, ROLE_CONFIG));

}

}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
SmileBits
SmileBits
智能合约安全审计