Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new rule to check length of array #45

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ no-bidi-characters | Generic | The code must not contain any of Unicode Directio
delegatecall-to-arbitrary-address | Generic | An attacker may perform delegatecall() to an arbitrary address.
incorrect-use-of-blockhash | Generic | blockhash(block.number) and blockhash(block.number + N) always returns 0.
accessible-selfdestruct | Generic | Contract can be destructed by anyone in $FUNC
missing-array-lengths-check | Generic | User activities may not be completed successfully if the arrays' lengths aren't determined to be the same.

## Gas Optimization Rules

Expand Down
76 changes: 76 additions & 0 deletions solidity/security/missing-array-lengths-check.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
pragma solidity ^0.8.0;

contract ArrayLength {

function completeQueuedWithdrawals(
//ok: missing-array-lengths-check
QueuedWithdrawal[] calldata queuedWithdrawals,
//ok: missing-array-lengths-check
uint256[] calldata middlewareTimesIndexes,
//ok: missing-array-lengths-check
bytes32[] calldata validatorFields
) external
{
for(uint256 i = 0; i < queuedWithdrawals.length; i++) {
_completeQueuedWithdrawal(queuedWithdrawals[i], tokens[i], middlewareTimesIndexes[i], receiveAsTokens[i]);
}

require(queuedWithdrawals.length == middlewareTimesIndexes.length, "different lengths");
require(middlewareTimesIndexes.length == queuedWithdrawals.length, "different lengths");


}


function verifyAndProcessWithdrawal(
BeaconChainProofs.WithdrawalProofs calldata withdrawalProofs,
bytes calldata validatorFieldsProof,
//ruleid: missing-array-lengths-check
bytes32[] calldata validatorFields,
//ruleid: missing-array-lengths-check
bytes32[] calldata withdrawalFields,
//ruleid: missing-array-lengths-check
uint256[] beaconChainETHStrategyIndex,
uint64 oracleBlockNumber
)
external
{

uint40 validatorIndex = uint40(Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_INDEX_INDEX]));

require(validatorStatus[validatorIndex] != VALIDATOR_STATUS.INACTIVE,
"EigenPod.verifyOvercommittedStake: Validator never proven to have withdrawal credentials pointed to this contract");

bytes32 beaconStateRoot = eigenPodManager.getBeaconChainStateRoot(oracleBlockNumber);

BeaconChainProofs.verifyWithdrawalProofs(beaconStateRoot, withdrawalProofs, withdrawalFields);
BeaconChainProofs.verifyValidatorFields(validatorIndex, beaconStateRoot, validatorFieldsProof, validatorFields);

uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);

//check if the withdrawal occured after mostRecentWithdrawalBlockNumber
uint64 slot = Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot);

(validatorFields[BeaconChainProofs.VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
if (Endian.fromLittleEndianUint64(validatorFields[BeaconChainProofs.VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]) <= slot/BeaconChainProofs.SLOTS_PER_EPOCH) {
_processFullWithdrawal(withdrawalAmountGwei, validatorIndex, beaconChainETHStrategyIndex, podOwner, validatorStatus[validatorIndex]);
} else {
_processPartialWithdrawal(slot, withdrawalAmountGwei, validatorIndex, podOwner);
}
}

function addChainlinkOracles(
//ok: missing-array-lengths-check
address[] memory tokens,
//ok: missing-array-lengths-check
address[] memory oracles,
//ok: missing-array-lengths-check
uint48[] memory heartbeats
) external {
if (tokens.length != oracles.length || oracles.length != heartbeats.length) {
revert InvalidLength();
}
// ...
}

}
55 changes: 55 additions & 0 deletions solidity/security/missing-array-lengths-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
rules:
- id: missing-array-lengths-check
message: User activities may not be completed successfully if the arrays' lengths aren't determined to be the same.
metadata:
category: security
tags:
- array
- length
patterns:
- pattern: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is very vague, $VAR and $VAR2 may not be related to each other. This rule tries to find an absence of some conditions which is not robust since you have to describe all situations.
With that said, I am not sure how to approach this vulnerability in some other way using semgrep 🤔

...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
require(<... $VAR.length ...>, ...);
...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
require(<... $VAR2.length ...>, ...);
...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
assert(<... $VAR.length ...>, ...);
...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
assert(<... $VAR2.length ...>, ...);
...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
if(<... $VAR.length ...>) {...}
...
}
- pattern-not: |
function $F(..., $TYPE[] $VAR, ..., $TYPE2[] $VAR2, ...) {
...
if(<... $VAR2.length ...>){...}
...
}
- focus-metavariable:
- $VAR
- $VAR2
languages:
- solidity
severity: INFO