ethereum / solidity

Solidity, the Smart Contract Programming Language
https://soliditylang.org
GNU General Public License v3.0
22.92k stars 5.68k forks source link

Overflow operations are not detected when enabling via-ir compilation #15249

Open lum7na opened 1 month ago

lum7na commented 1 month ago

Description

Hi! The following code does not result in an arithmetic overflow (the multiplication in the code) when via-ir compilation is enabled, and the return value of g() is wrong. I believe this overflow should occur. Specifically, if I remove the f() function, the correct code causing overflow is generated. Could this be due to the return in the inline assembly? The return opcode appears to have been misplaced.

contract Test {

  function f() private returns(uint256) {
    assembly  {
      return(0x40, 0x20)
    }
  }

  function g() public payable returns(uint256) {
    if (f() + uint256(keccak256(hex"00")) * 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff >= 0) {
      return 1;
    }
    return 0;
  }
}

It appears that the g() function has been discarded as a dead block.

sub_0: assembly {
        /* "out.sol":0:320  contract Test {... */
      mstore(0x40, 0x80)
      jumpi(tag_26, iszero(lt(calldatasize, 0x04)))
    tag_27:
      tag_9
      jump  // in
    tag_26:
      tag_28
      calldataload(0x00)
      tag_1
      jump  // in
    tag_28:
      0xe2179b8e
      sub
      tag_27
      jumpi
      tag_8
      jump  // in
    tag_1:
      0xe0
      shr
      swap1
      jump  // out
    tag_3:
      0x00
      dup1
      revert
    tag_4:
      0x00
      swap2
      sub
      slt
      tag_31
      jumpi
      jump  // out
    tag_31:
      tag_3
      jump  // in
    tag_8:
      tag_35
      calldatasize
      0x04
      tag_4
      jump  // in
    tag_35:
      tag_24
      jump  // in
    tag_9:
      0x00
      dup1
      revert
    tag_10:
      0x00
      swap1
      jump  // out
        /* "out.sol":113:318  function g() public payable returns(uint256) {... */
    tag_24:
        /* "out.sol":149:156  uint256 */
      tag_59
      tag_10
      jump  // in
    tag_59:
        /* "out.sol":168:171  f() */
      pop
        /* "out.sol":48:55  uint256 */
      tag_60
      tag_10
      jump  // in
    tag_60:
        /* "out.sol":63:105  assembly  {... */
      return(0x40, 0x20)

    auxdata: 0xa264697066735822122014b5cfb858122f0be48cf19a684cce82ebd7bec7b8a7ce6343ef1443e62532c764736f6c637827302e382e32372d646576656c6f702e323032342e372e382b636f6d6d69742e64343266393262640058
}

Environment

ekpyron commented 1 month ago

Legacy code generation does not have any guarantees about the evaluation order of complex expressions - and in this case, it evaluates the right summand before the left summand, s.t. the overflow error happens before the call to f(). via-IR code generation evaluates left-to-right, which means f will be called before the overflow error occurs and since it assembly-returns, the overflow check becomes unreachable. These subtle differences are specified in https://docs.soliditylang.org/en/latest/ir-breaking-changes.html