Mitigation Strategy: Checks-Effects-Interactions Pattern
Description:
- Checks: Begin each function (especially those interacting with external contracts or handling value) with a series of
require
statements to validate all preconditions and inputs. Examples:require(balanceOf[msg.sender] >= amount, "Insufficient balance");
require(input > 0 && input < maxValue, "Invalid input");
require(msg.sender == owner, "Unauthorized");
- Effects: After all checks, update the contract's state (modify balances, change ownership, etc.). Do this before external calls.
- Interactions: Finally, interact with other contracts (make external calls).
List of Threats Mitigated:
- Reentrancy (Severity: Critical): Prevents recursive calls before state changes are complete.
- State Inconsistency Issues (Severity: High): Ensures consistent state before external interactions.
Impact:
- Reentrancy: Reduces risk to near zero if implemented correctly.
- State Inconsistency: Significantly reduces likelihood of state-related bugs.
Currently Implemented:
- Example: "Implemented in
withdraw()
function inBank.sol
, lines 100-115, and indeposit()
function, lines 70-80."
Missing Implementation:
- Example: "Missing in
processPayment()
function inPaymentProcessor.sol
, lines 200-220. External call is made before state update."
Mitigation Strategy: Reentrancy Guard (Mutex)
Description:
- Declare a Lock:
bool private _locked;
- Create a Modifier:
modifier nonReentrant() { require(!_locked, "Reentrancy detected"); _locked = true; _; // Function code executes here _locked = false; }
- Apply the Modifier: Add
nonReentrant
to vulnerable functions.
List of Threats Mitigated:
- Reentrancy (Severity: Critical): Prevents reentrant calls.
Impact:
- Reentrancy: Strong defense, especially in complex scenarios.
Currently Implemented:
- Example: "Implemented as
nonReentrant
modifier inUtils.sol
, line 20. Applied towithdraw()
andclaimReward()
inStaking.sol
."
Missing Implementation:
- Example: "Missing in
batchTransfer()
inToken.sol
, lines 150-170."
Mitigation Strategy: SafeMath (Solidity < 0.8.0) / Built-in Overflow Checks (Solidity >= 0.8.0)
Description (Solidity < 0.8.0):
- Import SafeMath:
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
- Use SafeMath:
using SafeMath for uint256;
- Replace Operators: Use
add
,sub
,mul
,div
instead of+
,-
,*
,/
.
Description (Solidity >= 0.8.0):
- No Action (Usually): Built-in checks are active.
unchecked
Blocks (Rare, Use with Caution):unchecked { // Operations here will NOT revert on overflow/underflow }
List of Threats Mitigated:
- Arithmetic Overflow/Underflow (Severity: High): Prevents integer overflows/underflows.
Impact:
- Arithmetic Overflow/Underflow: Eliminates (>= 0.8.0) or significantly reduces (< 0.8.0) the risk.
Currently Implemented:
- Example (>= 0.8.0): "Project uses Solidity 0.8.17; built-in checks are active."
Missing Implementation:
- Example (< 0.8.0): "SafeMath not used in
calculateReward()
inRewards.sol
, lines 80-90." - Example (>= 0.8.0): "
unchecked
block used without justification inMathUtils.sol
."
Mitigation Strategy: Avoid Unbounded Loops (Gas Limit Handling)
Description:
- Identify Loops: Examine all loops, especially those over arrays/mappings.
- Determine Boundedness: Is the data structure size bounded or unbounded?
- Implement Limits (for unbounded loops):
- Fixed-Size Arrays: Use fixed-size arrays if possible.
- Pagination: Process a limited number of elements per transaction.
- User-Defined Limits: Allow users to specify a maximum (with a contract-enforced upper bound).
- Gas Cost Estimation: Estimate gas cost and revert if it exceeds a limit.
List of Threats Mitigated:
- Gas Limit DoS (Severity: High): Prevents running out of gas due to large loops.
- Block Gas Limit DoS (Severity: High): Reduces risk of consuming the entire block gas limit.
Impact:
- Gas Limit DoS / Block Gas Limit DoS: Significantly reduces DoS risk.
Currently Implemented:
- Example: "Pagination implemented in
getAllUsers()
inUserManager.sol
."
Missing Implementation:
- Example: "
processRewards()
inRewards.sol
iterates over an unbounded array."
Mitigation Strategy: Input Validation (using require
)
Description:
- Identify Inputs: All external function parameters and data from external calls.
- Define Constraints: Valid range, type, and format for each input.
- Implement Checks: Use
require
statements at the start of functions:require(amount > 0, "Amount must be positive");
require(userAddress != address(0), "Invalid address");
require(data.length <= MAX_DATA_LENGTH, "Data too long");
- Sanitize Data: If needed (e.g., remove whitespace).
List of Threats Mitigated:
- Various (Severity: Variable): Mitigates many vulnerabilities, including (indirectly) reentrancy, overflow/underflow, DoS, and logic errors.
- Short Address Attack (Severity: High): Validate address length.
Impact:
- Various: Significantly reduces attack surface.
Currently Implemented:
- Example: "Input validation on all function parameters in
Token.sol
andExchange.sol
."
Missing Implementation:
- Example: "Missing validation for
description
increateProposal()
inDAO.sol
."
Mitigation Strategy: Handle External Call Failures (Check Return Values)
Description:
- Identify Low-Level Calls: Find all
call
,delegatecall
, andsend
. - Check Return Values: Immediately after each call, check the boolean return:
(bool success, ) = targetAddress.call{value: amount}(""); require(success, "External call failed");
- Handle Failure: If
success
isfalse
, revert, log an event, or try an alternative. - Consider
try/catch
(Solidity >= 0.6.0):try IExternalContract(targetAddress).someFunction{value: amount}() { // Success } catch Error(string memory reason) { // Handle revert } catch (bytes memory lowLevelData) { // Handle low-level data }
List of Threats Mitigated:
- Unhandled Exceptions (Severity: High): Prevents inconsistent state after failed calls.
- DoS (Severity: Medium): Prevents exploiting unhandled exceptions.
Impact:
- Unhandled Exceptions / DoS: Significantly reduces risk.
Currently Implemented:
- Example: "All low-level calls in
PaymentProcessor.sol
have return values checked."
Missing Implementation:
- Example: "
sendEther()
inUtils.sol
doesn't checksend()
return value."
Mitigation Strategy: Careful use of delegatecall
Description:
- Understand the Risks:
delegatecall
executes code in the context of the calling contract's storage. The called contract can overwrite the caller's state. - Use with Trusted Contracts Only: Only use
delegatecall
with contracts you fully trust and control. Never use it with an address provided by a user. - Storage Layout Compatibility: If you must use it, ensure storage layouts are compatible to prevent data corruption.
- Consider Libraries/Proxies: Use established patterns like upgradeable contracts (proxies) that handle
delegatecall
safely.
List of Threats Mitigated:
- Malicious Code Execution via
delegatecall
(Severity: Critical): Prevents attackers from hijacking control flow and modifying your contract's state. - Storage Corruption (Severity: High): Ensures that the called contract doesn't accidentally overwrite important data.
Impact:
- Malicious Code Execution / Storage Corruption: Reduces the risk to near zero if used correctly (i.e., only with trusted contracts).
Currently Implemented:
- Example: "
delegatecall
is only used within the proxy upgrade pattern, with a trusted implementation contract." - Example: "No instances of
delegatecall
are currently used in the project."
Missing Implementation:
- Example: "
delegatecall
is used incallUntrustedContract()
inVulnerable.sol
, taking a user-provided address as input. This is a critical vulnerability."