Solidity lang Compiler | Bringing back Arithmetic bug over ^0.8.0 version.
April 10th, 2024

Intro:

This write-up will explain my accidental discovery of a solidity compiler bug. During a security audit on a protocol, I came across an interesting and Known compiler bug on solidity. I quickly escalated to the Ethereum security team and got a well-expected response.

Compiler version affected:

0.8.19+commit.7dd6d404 and the latest release

Target EVM Version:

Default on Remix

Framework/IDE:

Remix IDE

Description:

Solidity version 0.8.0 or above promises to prevent arithmetic overflow and underflow errors by default.

But In certain circumstances, It throws unexpected results by producing large Unsigned integers when it is used with a Signed Integer. (int256)

I came across a protocol that uses signed and unsigned integers for subtracting, where its VARIABLE A and B are declared as int256 and subtraction is performed inside the type of unsigned integers as uint256(A  - B).

Here I got an idea to test for arithmetic error, as I expected a large number to be produced by declaring Variable A lesser than Variable B and thus solidity program doesn't revert and produces (uint256.max) - (-C).

Here (-C) is the output value of int256 A - B.

A protocol, that I was involved in a security audit, uses it for reward calculation and fortunately, it is not exploitable by the nature of its design. ๐Ÿ˜ฎโ€๐Ÿ’จ

Sample of Pseudovulnerable code:

struct UserInfo {
        int256 rewardDebt;
    }
        int256 _accumulatedReward = int256((user.amount * accRewardPerShare) / ACC_REWARD_PRECISION);
        uint256 _pendingRewards = uint256(_accumulatedReward - user.rewardDebt);

Input program that triggers the bug:


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract testMath {
    //Numbers to Test
    int256 a = 1;
    int256 b = 10;

    // This will be succeeded and Returns (uint256.max - 9)
    function tests() public returns (uint256) {
        uint256 o = uint256(a - b);
        return o;
    }
    
}

Expected Behaviour:

The uint256(A - B) should be aliased as uint256(a) - uint256(b) whether it's written as uint256(A-B).

pragma solidity ^0.8.0;

contract testMathRevert {
    //Numbers to Test
    int256 a = 1;
    int256 b = 10;

    // This will be Reverted;
    function tests() public returns (uint256) {
        uint256 o = uint256(a) - uint256(b);
        return o;
    }
    
}

Response from the Ethereum Security team:

This is a known issue and has been previously reported. It is scheduled to be addressed eventually to improve the situation for conversions, making different conversion behaviour (e.g. truncating vs reverting) explicit: https://github.com/ethereum/solidity/issues/11284

Subscribe to Shanmuga Bharathi
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from Shanmuga Bharathi

Skeleton

Skeleton

Skeleton