- Contract uses loops with boundary conditions, comparison operators at thresholds, or array index calculations
// Skips the last element
function processAll() external {
for (uint256 i = 0; i < users.length - 1; i++) {
// Should be i < users.length
// Last user is never processed
_distribute(users[i]);
}
}
// Off-by-one in threshold check
function liquidate(uint256 ratio) external {
// Should be ratio < MIN_RATIO (liquidate when below)
// Using <= means accounts AT the minimum are also liquidated
require(ratio <= MIN_RATIO, "healthy");
_liquidate();
}
// Out-of-bounds access
function getLastUser() external view returns (address) {
return users[users.length]; // Should be users.length - 1
}
< length vs <= length vs < length - 1< length - 1 skips the last element — flag unless intentional<= length goes out of bounds on array access — flag always>, >=, <, <=), verify the boundary matches the specification (e.g., "greater than" vs "greater than or equal to")length - 1 on arrays that could be empty — this underflows to type(uint256).max in unchecked contexts or reverts in checked contexts< length - 1 to skip the sentinel/last element by design)i < length - 1 to compare arr[i] with arr[i+1])length > 0 before length - 1function processAll() external {
for (uint256 i = 0; i < users.length; i++) {
_distribute(users[i]);
}
}
// Explicit about boundary semantics
function liquidate(uint256 ratio) external {
require(ratio < MIN_RATIO, "healthy"); // Strictly below = liquidatable
_liquidate();
}