Skip to main content

整数溢出

原理

在整数超出位数的上限或下限时,就会静默地进行取模操作。通常我们希望费用向上溢出变小,或者存款向下溢出变大。整数溢出漏洞可以使用 SafeMath 库来防御,当发生溢出时会回滚交易。

例如:

uint8 的最大值是 255,最小值是 0

pragma solidity 0.5.16;

contract Flow {
uint8 balance = 10;
// return: 9
function overFlow() external view returns (uint8) {
return balance + 255;
}

// return: 254
function underFlow() external view returns (uint8) {
return balance - 12;
}
}

由于向上溢出和向下溢出的问题,会有如下攻击案例: Token

默认情况下,账户有 20 个 Token,需要通过某种方法增加手中的 token 数量

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

contract Token {

mapping(address => uint) balances;
uint public totalSupply;

constructor(uint _initialSupply) public {
balances[msg.sender] = totalSupply = _initialSupply;
}

function transfer(address _to, uint _value) public returns (bool) {
require(balances[msg.sender] - _value >= 0);
balances[msg.sender] -= _value;
balances[_to] += _value;
return true;
}

function balanceOf(address _owner) public view returns (uint balance) {
return balances[_owner];
}
}

由于transfer方法中,直接对balance进行了加减运算,通过balances[msg.sender] -= _value;向下溢出,即可使我们手中的 token 数量增加

solidity 0.8.0 之后的版本规避了这个问题。如果不使用unchecked关键字,则会报错。如果使用unchecked关键字,依然是有溢出问题.例如

pragma solidity 0.8.3;

contract Flow {
uint8 balance = 10;
// return: 9
function overFlow() external view returns (uint8) {
unchecked { return balance + 255; }
}

// return: 254
function underFlow() external view returns (uint8) {
unchecked{return balance - 12;}
}
}