整数溢出攻击分为两类,一类是上溢,一类是下溢。由于现在的大部分合约都使用uint256作为数据类型,因此上溢需要很大数量的代币,因此本文讨论的均为下溢。
在Solidity语言中,变量支持的整数类型步长以8递增,支持从uint8到uint256,以及int8到int256。例如,一个 uint8类型 ,只能存储在范围 0到2^8-1,也就是[0,255] 的数字,一个 uint256类型 ,只能存储在范围 0到2256-1的数字。在以太坊虚拟机(EVM)中为整数指定固定大小的数据类型,而且是无符号的,这意味着在以太坊虚拟机中一个整型变量只能有一定范围的数字表示,不能超过这个制定的范围。超过制定的范围就会出现整数溢出,分为整数上溢和整数下溢。以整数下溢为例:(uint256)0 - 1 = (uint256) 2^256-1,在最小值上减1,直接变成最大值。
以下面代码为例:
pragma solidity ^0.4.22;
//存在整数溢出
contract iof{
mapping(address => uint) balances;
event balancesAndAmount(uint, uint);
function balanceof(address _user) returns(uint) {return balances[_user];}
function depoist() payable {balances[msg.sender] += msg.value;}
function withdraw(uint _amount){
balancesAndAmount(balances[msg.sender],_amount);
require(balances[msg.sender] - _amount > 0); //存在整数溢出
msg.sender.transfer(_amount);
balances[msg.sender] -= _amount;
}
首先将有漏洞的合约部署上私链,用钱包1向合约中充值10gwei,接着用钱包2向合约中充值1wei,然后用钱包2调用withdraw函数取出2wei, 合约执行成功,并可见合约中的余额变成了9wei,而不是应该的10wei,实现了合约的攻击。


(1) 使用transfer()函数:由于transfer转账功能只能发送2300gas不足以使目的地址/合约调用另外一份合约(即重入发送合约);
(2) 引入互斥锁。也就是说,要添加一个在代码执行过程中锁定合约的状态变量,阻止重入调用。
