26 Кві, 2023

Assert Violation (SWC-110)

Description

The Solidity assert() function is used to check for invariants in the code. Invariants are conditions that should always hold true, and if they don’t, it indicates that something has gone wrong in the code. When the assert statement is executed, if the condition it checks is false, the contract execution is halted and any remaining gas is refunded to the caller.

A failing assert statement indicates that the contract has entered an unexpected or invalid state. This could mean that there is a bug in the contract code that allowed it to enter that state, or that there is an external factor causing the invalid state. It is important to investigate the reason for the failing assert statement and fix any bugs that may exist in the contract.

It is also possible that the assert statement is being used incorrectly. For example, it may be used to validate user inputs, which should be checked using require statements instead. In this case, the assert statement is not checking an invariant of the code, but rather an input that should have been validated before executing the contract. In such cases, the use of assert can result in a security vulnerability, as it could allow an attacker to exploit an input validation flaw in the contract. Therefore, it is important to use assert statements correctly and only to check for invariants in the code.

Remediation

To address SWC-110, one approach is to review the condition being checked in the assert() statement and determine whether it is actually an invariant or not. If it is not an invariant, it should be replaced with a require() statement. The require() statement is used to check inputs and conditions that must hold for the function to execute properly, but are not necessarily invariants.

If the condition is an invariant, then the assertion should not be reachable. If it is reachable, then there is likely a bug in the code that needs to be fixed. Properly functioning code should never reach a failing assert statement, so if it does, it indicates that there is a problem with the code that needs to be addressed.

Contract Samples

Code with a vulnerability

				
					pragma solidity ^0.4.22;

contract TwoMappings{

    mapping(uint=>uint) m;
    mapping(uint=>uint) n;

    constructor(){
        m[10] = 100;
    }
    
    function check(uint a){
        assert(n<a href='#'   ></a> == 0);
    }
}
				
			

Code without a vulnerability

				
					contract SimpleDSChief {
    mapping(bytes32=>address) public slates;
    mapping(address=>bytes32) public votes;
    mapping(address=>uint256) public approvals;
    mapping(address=>uint256) public deposits;

    function lock(uint wad) public {
        deposits[msg.sender] = add(deposits[msg.sender], wad);
        addWeight(wad, votes[msg.sender]);
    }

    function free(uint wad) public {
        deposits[msg.sender] = sub(deposits[msg.sender], wad);
        subWeight(wad, votes[msg.sender]);
    }

    function voteYays(address yay) public returns (bytes32){
        bytes32 slate = etch(yay);
        voteSlate(slate);

        return slate;
    }

    function etch(address yay) public returns (bytes32 slate) {
        bytes32 hash = keccak256(abi.encodePacked(yay));

        slates[hash] = yay;

        return hash;
    }

    function voteSlate(bytes32 slate) public {
        uint weight = deposits[msg.sender];
        subWeight(weight, votes[msg.sender]);
        votes[msg.sender] = slate;
        addWeight(weight, votes[msg.sender]);
    }

    function addWeight(uint weight, bytes32 slate) internal {
        address yay = slates[slate];
        approvals[yay] = add(approvals[yay], weight);
    }

    function subWeight(uint weight, bytes32 slate) internal {
        address yay = slates[slate];
        approvals[yay] = sub(approvals[yay], weight);
    }

    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x);
    }

    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x);
    }

   function checkAnInvariant() public {
        bytes32 senderSlate = votes[msg.sender];
        address option = slates[senderSlate];
        uint256 senderDeposit = deposits[msg.sender];
        
        assert(approvals[option] >= senderDeposit);
    }
}
				
			

Tools for scaning SWC-110

1. Mythril – an open-source security analysis tool for Ethereum smart contracts that can detect various types of security vulnerabilities, including SWC-110.

2. Securify – a tool that analyzes Solidity smart contracts for security vulnerabilities, including SWC-110, by applying static analysis techniques.

3. SmartCheck – a security scanner that uses static analysis to identify security issues in Solidity code, including SWC-110.

4. Slither – an open-source static analysis tool for Solidity that can detect SWC-110 and other security vulnerabilities.

Загальна перерахування слабких місць (CWE)

CWE-670: Always-Incorrect Control Flow Implementation

Mitigation for SWC-110

1. Use require() instead of assert() for input validation: While assert() is useful for detecting unexpected behavior and invariant violations, require() is a better choice for validating input conditions. require() should be used to validate input conditions where it is possible for the caller of the function to provide incorrect or malicious inputs.

2. Avoid integer overflows and underflows: One of the most common causes of assert() failures is integer overflows and underflows. To avoid these issues, you should always use safe arithmetic functions like SafeMath or the built-in Solidity overflow checks.

3. Check input values for expected range: Before performing any arithmetic or comparison operation on input values, you should check that they are within the expected range. This can help prevent unintended behavior caused by incorrect input values.

4. Use appropriate data types: Using the appropriate data types for your variables can also help prevent unintended behavior. For example, using uint256 instead of uint32 for a variable that may need to hold a large value can help prevent overflow issues.

5. Write comprehensive tests: It is important to write comprehensive tests to ensure that your smart contract functions as expected and to catch any unexpected behavior. Tests should include both positive and negative scenarios, including edge cases.

6. Use static analysis tools: Static analysis tools like Mythril and Securify can help detect potential vulnerabilities in your smart contract code, including SWC-110 issues.

Висновок

SWC-110 is a vulnerability related to the misuse of the assert() statement in Solidity. The assert() statement is meant to check for invariants that should never be violated, and its failure should not be recoverable. However, if a reachable assert() statement fails, it could indicate either a bug that allows the contract to enter an invalid state or the incorrect use of the assert() statement to validate inputs.

To mitigate SWC-110, developers should consider whether the condition checked by the assert() statement is an invariant. If it is not, they should replace the assert() statement with a require() statement. If the exception is indeed caused by unexpected behavior of the code, the developers should fix the underlying bug(s) that allow the assertion to be violated.

To scan for this vulnerability, several static analysis tools can be used, such as Mythril, Slither, or Securify. Additionally, it is recommended to perform manual testing of the smart contract, following a well-defined testing plan.

Інші Послуги

Готові до безпеки?

зв'язатися з нами