File location:
Summary
The 'initNexus' function in the contract does not verify the conformity of the lengths of the 'validators' and 'executors' arrays. This can cause a mismatch in the number of iterations which can result in incomplete execution and potential failure in the validator and executor configuration.
Vulnerability Details
In the 'initNexus' function, the lengths of the 'validators' and 'executors' arrays are not verified whether they are the same or not before iterating and installing the related modules. If the array lengths are unequal, iterations on the shorter array will complete faster while elements on the longer array will not be accessed, causing inconsistent configuration.
Impact
If the lengths of the 'validators' and 'executors' arrays are not the same, this can cause some modules to not be installed correctly. This can result in failures in the validation process and incomplete execution, potentially creating security risks and operational failures in applications using these contracts.
Tools Used
Inspection manual
Solidity
Foundry
Recommendations
To fix this, you can add an array length check at the start of the relevant function. This check ensures that all operated arrays are of appropriate length before performing further operations.
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);
}
L119-L129
function getInitNexusCalldata(
BootstrapConfig[] calldata validators,
BootstrapConfig[] calldata executors,
BootstrapConfig calldata hook,
BootstrapConfig[] calldata fallbacks,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external view returns (bytes memory init) {
init = abi.encode(address(this), abi.encodeCall(this.initNexus, (validators, executors, hook, fallbacks, registry, attesters, threshold)));
}
L135-L143
function getInitNexusScopedCalldata(
BootstrapConfig[] calldata validators,
BootstrapConfig calldata hook,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external view returns (bytes memory init) {
init = abi.encode(address(this), abi.encodeCall(this.initNexusScoped, (validators, hook, registry, attesters, threshold)));
}
Code fixed and used for testing using Foundry:
struct BootstrapConfig {
address module;
bytes data;
}
contract RegistryBootstrap {
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 == executors.length, "Validators and executors arrays must have the same length");
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 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);
}
function getInitNexusCalldata(
BootstrapConfig[] calldata validators,
BootstrapConfig[] calldata executors,
BootstrapConfig calldata hook,
BootstrapConfig[] calldata fallbacks,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external view returns (bytes memory init) {
init = abi.encode(address(this), abi.encodeCall(this.initNexus, (validators, executors, hook, fallbacks, registry, attesters, threshold)));
}
function getInitNexusScopedCalldata(
BootstrapConfig[] calldata validators,
BootstrapConfig calldata hook,
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
) external view returns (bytes memory init) {
init = abi.encode(address(this), abi.encodeCall(this.initNexusScoped, (validators, hook, 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 {
}
}
Explanation:
In the code above, I added an array length check by using ‘require’ in the ‘initNexus’ function. This check ensures that the lengths of the ‘validators’ and ‘executors’ arrays are the same before performing further operations. If the array lengths are not the same, the transaction will be aborted and an error message will be displayed.
Foundry output:
Ran 3 tests for test/RegistryBootstrapTest.sol:RegistryBootstrapTest
[PASS] testInitNexus() (gas: 17216)
[PASS] testInitNexusArrayLengthMismatch() (gas: 16094)
[PASS] testInitNexusScoped() (gas: 10554)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 50.81ms (22.70ms CPU time)
Ran 1 test suite in 105.87ms (50.81ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)