整数溢出
原理
在整数超出位数的上限或下限时,就会静默地进行取模操作。通常我们希望费用向上溢出变小,或者存款向下溢出变大。整数溢出漏洞可以使用 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;}
}
}