code-423n4 / 2023-05-ajna-findings

2 stars 0 forks source link

Overflow/Underflow in the executeExtraordinary() function in ExtraordinaryFunding.sol #51

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L56

Vulnerability details

Impact

executeExtraordinary function, the tokensRequested variable is cast from a uint128 to a uint256, but there is no check to ensure that the cast doesn't result in an overflow. If an integer overflow/underflow occurs in a program, it can lead to unexpected and potentially catastrophic behavior. In some cases, it may cause the program to crash or behave in unintended ways. In other cases, it may lead to security vulnerabilities, allowing attackers to exploit the flaw to gain access to sensitive data or to take control of the system.

For example, in the context of cryptocurrencies, integer overflows/underflows can potentially result in the creation or destruction of tokens, leading to significant financial losses for users.

Proof of Concept

Here's an PoC that demonstrates the issue:

pragma solidity ^0.8.0;

contract IntegerOverflowUnderflow {

    uint128 public tokensRequested = 2**128 - 1;
    uint256 public tokensAvailable = 1000;

    function executeExtraordinary() public returns(bool success) {
        require(tokensRequested <= tokensAvailable, "Not enough tokens available");

        uint256 tokensToTransfer = uint256(tokensRequested);

        tokensAvailable -= tokensToTransfer;

        return true;
    }
}

In this PoC, we have set the tokensRequested variable to the maximum value of a uint128 (2^128-1), which is greater than the maximum value that can be represented by a uint256 (2^256-1). When the executeExtraordinary function is called, the tokensRequested variable is cast to a uint256 without any check to ensure that it doesn't result in an overflow.

As a result, when the cast is performed, the tokensToTransfer variable will have a value of 0, because the value of tokensRequested exceeds the maximum value that can be represented by a uint256. This means that no tokens will be transferred, even though there are enough tokens available.

To mitigate this issue, you can add a check to ensure that the value of tokensRequested does not exceed the maximum value of a uint256 before casting it. For example:

function executeExtraordinary() public returns(bool success) {
    require(tokensRequested <= tokensAvailable, "Not enough tokens available");

    require(uint256(tokensRequested) <= 2**256 - 1, "Integer overflow");

    uint256 tokensToTransfer = uint256(tokensRequested);

    tokensAvailable -= tokensToTransfer;

    return true;
}

In this modified version of the function, we have added a check to ensure that the cast from uint128 to uint256 doesn't result in an overflow. The check ensures that the value of tokensRequested is less than or equal to the maximum value that can be represented by a uint256 (2^256-1) before casting it.

Tools Used

Recommended Mitigation Steps

There are several ways to mitigate the risk of integer overflow/underflow:

  1. Use larger integer data types: Instead of using a smaller data type, such as uint128 or uint256, you can use larger data types such as uint256 or uint512. This can reduce the likelihood of integer overflow/underflow.

  2. Bounds checking: You can add bounds checking to ensure that values don't exceed the maximum or minimum values that can be represented by the data type. For example, if you are working with a uint256, you can check if the value is greater than 2^256 - 1 before performing an operation that could cause overflow.

  3. Defensive programming: You can add defensive programming techniques to prevent integer overflow/underflow. For example, you can add assertions or precondition checks to ensure that variables have valid values before using them in an operation.

  4. Use libraries: You can use libraries that have been developed specifically to mitigate the risk of integer overflow/underflow. For example, the SafeMath library in Solidity provides functions for performing arithmetic operations that prevent overflow/underflow.

  5. Use language features: Some programming languages have features built-in to mitigate the risk of integer overflow/underflow. For example, Rust has built-in support for checked arithmetic, which returns an error when an overflow/underflow occurs.

Here's an example of how you could add this check to the executeExtraordinary function:

function executeExtraordinary(uint128 tokensRequested) public { require(tokensRequested <= uint256(-1), "Tokens requested exceeds maximum uint256 value"); uint256 tokens = uint256(tokensRequested); // rest of the function code }

This code uses the maximum value of a uint256, which is represented by the expression uint256(-1). If the value of tokensRequested is greater than this maximum value, the function will revert with an error message indicating that the tokens requested exceeds the maximum uint256 value.

Assessed type

Under/Overflow

c4-judge commented 1 year ago

Picodes marked the issue as unsatisfactory: Invalid