File location:
Summary
Using loops in public or external functions in Solidity can lead to high gas costs and potential Denial of Service (DoS) attacks. A DoS attack can occur when an attacker intentionally exploits the gas costs of a function, causing it to run out of gas or making it too expensive for other users to call.
Vulnerability Details
Loops in public or external functions can process large amounts of data in a single transaction, leading to very high gas fees. If the loop is not properly constrained, an attacker can send very large amounts of data to force the function to run out of gas, making it unusable by other users. Additionally, nested loops or double loops can exacerbate this problem.
Impact
The function becomes inaccessible to other users due to running out of gas.
Gas costs are very high, making the function uneconomical to use.
Damage or disruption to smart contract services that depend on such functionality.
Tools Used
Inspection manual
Solidity
Foundry
Recommendations
Limit the input size. Make sure the size of the array received by the function is not too large. This can be done by limiting the input length on the frontend side or by checking the array length in the contract.
Code snippet:
L55-L87
function initNexus(
BootstrapConfig[] calldata validators,
BootstrapConfig[] calldata executors,
BootstrapConfig calldata hook,
BootstrapConfig[] calldata fallbacks,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external {
for (uint256 i = 0; i < validators.length; i++) {
_installValidator(validators[i].module, validators[i].data);
}
for (uint256 i = 0; i < executors.length; i++) {
if (executors[i].module == address(0)) continue;
_installExecutor(executors[i].module, executors[i].data);
}
if (hook.module != address(0)) {
_installHook(hook.module, hook.data);
}
for (uint256 i = 0; i < fallbacks.length; i++) {
if (fallbacks[i].module == address(0)) continue;
_installFallbackHandler(fallbacks[i].module, fallbacks[i].data);
}
_configureRegistry(registry, attesters, threshold);
}
L93-L111
function initNexusScoped(
BootstrapConfig[] calldata validators,
BootstrapConfig calldata hook,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external {
for (uint256 i = 0; i < validators.length; i++) {
_installValidator(validators[i].module, validators[i].data);
}
if (hook.module != address(0)) {
_installHook(hook.module, hook.data);
}
_configureRegistry(registry, attesters, threshold);
}
Fixed code:
uint256 constant MAX_ARRAY_LENGTH = 50;
function initNexus(
BootstrapConfig[] calldata validators,
BootstrapConfig[] calldata executors,
BootstrapConfig calldata hook,
BootstrapConfig[] calldata fallbacks,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external {
require(validators.length <= MAX_ARRAY_LENGTH, "Validators array too long");
require(executors.length <= MAX_ARRAY_LENGTH, "Executors array too long");
require(fallbacks.length <= MAX_ARRAY_LENGTH, "Fallbacks array too long");
for (uint256 i = 0; i < validators.length; i++) {
_installValidator(validators[i].module, validators[i].data);
}
for (uint256 i = 0; i < executors.length; i++) {
if (executors[i].module == address(0)) continue;
_installExecutor(executors[i].module, executors[i].data);
}
if (hook.module != address(0)) {
_installHook(hook.module, hook.data);
}
for (uint256 i = 0; i < fallbacks.length; i++) {
if (fallbacks[i].module == address(0)) continue;
_installFallbackHandler(fallbacks[i].module, fallbacks[i].data);
}
_configureRegistry(registry, attesters, threshold);
}
Code when testing using Foundry:
interface IERC7484 {
}
struct BootstrapConfig {
address module;
bytes data;
}
contract RegistryBootstrap {
uint256 constant MAX_ARRAY_LENGTH = 50;
function initNexus(
BootstrapConfig[] calldata validators,
BootstrapConfig[] calldata executors,
BootstrapConfig calldata hook,
BootstrapConfig[] calldata fallbacks,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external {
require(validators.length <= MAX_ARRAY_LENGTH, "Validators array too long");
require(executors.length <= MAX_ARRAY_LENGTH, "Executors array too long");
require(fallbacks.length <= MAX_ARRAY_LENGTH, "Fallbacks array too long");
for (uint256 i = 0; i < validators.length; i++) {
_installValidator(validators[i].module, validators[i].data);
}
for (uint256 i = 0; i < executors.length; i++) {
if (executors[i].module == address(0)) continue;
_installExecutor(executors[i].module, executors[i].data);
}
if (hook.module != address(0)) {
_installHook(hook.module, hook.data);
}
for (uint256 i = 0; i < fallbacks.length; i++) {
if (fallbacks[i].module == address(0)) continue;
_installFallbackHandler(fallbacks[i].module, fallbacks[i].data);
}
_configureRegistry(registry, attesters, threshold);
}
function _installValidator(address module, bytes calldata data) internal {
}
function _installExecutor(address module, bytes calldata data) internal {
}
function _installHook(address module, bytes calldata data) internal {
}
function _installFallbackHandler(address module, bytes calldata data) internal {
}
function _configureRegistry(IERC7484 registry, address[] calldata attesters, uint8 threshold) internal {
}
}
Foundry output:
Ran 4 tests for test/RegistryBootstrap.t.sol:RegistryBootstrapTest
[PASS] testExecutorsArrayTooLong() (gas: 38203)
[PASS] testFallbacksArrayTooLong() (gas: 38164)
[PASS] testInitNexus() (gas: 21143)
[PASS] testValidatorsArrayTooLong() (gas: 38132)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 51.48ms (35.66ms CPU time)
Ran 1 test suite in 107.19ms (51.48ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)