Description: The InheritanceManager contract uses a loop to check whether the caller is a beneficiary in the onlyBeneficiaryWithIsInherited modifier.
However, the longer the beneficiaries array, the more gas is consumed, which could lead to a potential denial of service (DoS) attack.
Additionally, removeBeneficiary does not reduce the length of the beneficiaries array, increasing the risk of a Denial-of-Service (DoS) attack.
modifier onlyBeneficiaryWithIsInherited() {
uint256 i = 0;
@> while (i < beneficiaries.length + 1) {
if (msg.sender == beneficiaries[i] && isInherited) {
break;
}
i++;
}
_;
}
...
function removeBeneficiary(address _beneficiary) external onlyOwner {
...
@> delete beneficiaries[indexToRemove];
...
}
Impact: The gas cost of the onlyBeneficiaryWithIsInherited modifier increases as the number of beneficiaries grows, impact function buyOutEstateNFT and appointTrustee
Proof of Concept: Add the following test and run
function test_DoS() public {
uint256 gasCost1 = gasCostMock(0);
uint256 gasCost2 = gasCostMock(100);
uint256 gasCost3 = gasCostMock(200);
assertLt(gasCost1, gasCost2);
assertLt(gasCost2, gasCost3);
console.log("gasCost1: %s", gasCost1);
console.log("gasCost2: %s", gasCost2);
console.log("gasCost3: %s", gasCost3);
}
function gasCostMock(uint256 loop) public returns (uint256 gasCost){
vm.startPrank(owner);
for(uint160 i; i < loop; i++){
im.addBeneficiery(beneficiary1);
im.addBeneficiery(beneficiary2);
im.removeBeneficiary(beneficiary1);
im.removeBeneficiary(beneficiary2);
}
im.addBeneficiery(beneficiary1);
im.addBeneficiery(beneficiary2);
vm.stopPrank();
vm.warp(im.getDeadline());
im.inherit();
uint256 gasStart = gasleft();
vm.prank(beneficiary1);
im.appointTrustee(otherUser);
gasCost = gasStart - gasleft();
}
Recommended Mitigation:
Use mapping(address->bool) to replace the array to check if the address is a beneficiary.
+ mapping(address => bool) public isBeneficiary;
...
modifier onlyBeneficiaryWithIsInherited() {
require(isBeneficiary(msg.sender), "Not a beneficiary");
require(isInherited, "Not yet inherited");
_;
}
function addBeneficiery(address _beneficiary) external onlyOwner {
...
+ isBeneficiary(_beneficiary) = true;
_setDeadline();
}
function removeBeneficiary(address _beneficiary) external onlyOwner {
...
+ isBeneficiary(_beneficiary) = false;
_setDeadline();
}