The arithmetic operation can overflow 是否有可能导致算术溢出?
The arithmetic operation can overflow is it possible to cause an arithmetic overflow?
我想为以下合约函数提供一些可能溢出的建议。断言或要求输入吐回“ParserError: Expected ',' but got identifier
assert (uint i = 0; i < _index + 1; i++) {"
我倾向于 require 实现,因为在合同开始时已经导入了安全的数学库。我见过许多具有相同实现的不同合同,但在确定正确方法时遇到了一些问题。非常感谢您的帮助。
算术运算可能会溢出。
可能会导致算术溢出。通过使用必需的 () 语句或用于整数算术运算的 OpenZeppelin SafeMath 库限制输入来防止溢出。参考为此问题生成的事务跟踪以重现溢出
我也会附上漏洞的复现
[重现此漏洞的说明(测试用例 1)]
contract FreezableToken is StandardToken {
// freezing chains
mapping (bytes32 => uint64) internal chains;
// freezing amounts for each chain
mapping (bytes32 => uint) internal freezings;
// total freezing balance per address
mapping (address => uint) internal freezingBalance;
event Freezed(address indexed to, uint64 release, uint amount);
event Released(address indexed owner, uint amount);
/**
* @dev Gets the balance of the specified address include freezing tokens.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return super.balanceOf(_owner) + freezingBalance[_owner];
}
/**
* @dev Gets the balance of the specified address without freezing tokens.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function actualBalanceOf(address _owner) public view returns (uint256 balance) {
return super.balanceOf(_owner);
}
function freezingBalanceOf(address _owner) public view returns (uint256 balance) {
return freezingBalance[_owner];
}
/**
* @dev gets freezing count
* @param _addr Address of freeze tokens owner.
*/
function freezingCount(address _addr) public view returns (uint count) {
uint64 release = chains[toKey(_addr, 0)];
while (release != 0) {
count++;
release = chains[toKey(_addr, release)];
}
}
/**
* @dev gets freezing end date and freezing balance for the freezing portion specified by index.
* @param _addr Address of freeze tokens owner.
* @param _index Freezing portion index. It ordered by release date descending.
*/
function getFreezing(address _addr, uint _index) public view returns (uint64 _release, uint _balance) {
for (uint i = 0; i < _index + 1; i++) { **<- Error Here ``< _index + 1; i++)``**
_release = chains[toKey(_addr, _release)];
if (_release == 0) {
return;
}
}
_balance = freezings[toKey(_addr, _release)];
}
/**
* @dev freeze your tokens to the specified address.
* Be careful, gas usage is not deterministic,
* and depends on how many freezes _to address already has.
* @param _to Address to which token will be freeze.
* @param _amount Amount of token to freeze.
* @param _until Release date, must be in future.
*/
function freezeTo(address _to, uint _amount, uint64 _until) public {
require(_to != address(0));
require(_amount <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender].sub(_amount);
bytes32 currentKey = toKey(_to, _until);
freezings[currentKey] = freezings[currentKey].add(_amount);
freezingBalance[_to] = freezingBalance[_to].add(_amount);
freeze(_to, _until);
emit Transfer(msg.sender, _to, _amount);
emit Freezed(_to, _until, _amount);
}
/**
* @dev release first available freezing tokens.
*/
function releaseOnce() public {
bytes32 headKey = toKey(msg.sender, 0);
uint64 head = chains[headKey];
require(head != 0);
require(uint64(block.timestamp) > head);
bytes32 currentKey = toKey(msg.sender, head);
uint64 next = chains[currentKey];
uint amount = freezings[currentKey];
balances[msg.sender] = balances[msg.sender].add(amount);
freezingBalance[msg.sender] = freezingBalance[msg.sender].sub(amount);
if (next == 0) {
} else {
chains[headKey] = next;
}
emit Released(msg.sender, amount);
}
/**
* @dev release all available for release freezing tokens. Gas usage is not deterministic!
* @return how many tokens was released
*/
function releaseAll() public returns (uint tokens) {
uint release;
uint balance;
(release, balance) = getFreezing(msg.sender, 0);
while (release != 0 && block.timestamp > release) {
releaseOnce();
tokens += balance;
(release, balance) = getFreezing(msg.sender, 0);
}
}
function toKey(address _addr, uint _release) internal pure returns (bytes32 result) {
// WISH masc to increase entropy
result = 0x5749534800000000000000000000000000000000000000000000000000000000;
assembly {
result := or(result, mul(_addr, 0x10000000000000000))
result := or(result, and(_release, 0xffffffffffffffff))
}
}
function freeze(address _to, uint64 _until) internal {
require (_until > block.timestamp);
bytes32 key = toKey(_to, _until);
bytes32 parentKey = toKey(_to, uint64(0));
uint64 next = chains[parentKey];
if (next == 0) {
chains[parentKey] = _until;
return;
}
bytes32 nextKey = toKey(_to, next);
uint parent;
while (next != 0 && _until > next) {
parent = next;
parentKey = nextKey;
next = chains[nextKey];
nextKey = toKey(_to, next);
}
if (_until == next) {
return;
}
if (next != 0) {
chains[key] = next;
}
chains[parentKey] = _until;
}
}```
[1]: https://i.stack.imgur.com/ayg2D.png
根据上下文,我假设错误消息“算术运算可能溢出。”来自静态分析工具。
假设您使用的是低于 0.8.0 的 Solidity 版本,for 循环定义在理论上容易受到整数溢出的影响。但前提是 _index
为 2^256,即 uint
的最大值。该值会使 _index + 1
表达式溢出。
仅导入 SafeMath 库是不够的。您还需要使用它的功能而不是原生算术运算来防止溢出。
contract FreezableToken is StandardToken {
// allows to use functions of the library on `uint` type
using SafeMath for uint;
function getFreezing() public {
// use the `add()` function of the library instead of the `+` operation
for (uint i = 0; i < _index.add(1); i = i.add(1)) {
或者升级到 Solidity 版本 0.8+,检查语言级别的溢出,这样您就不必使用 SafeMath 库了。
我想为以下合约函数提供一些可能溢出的建议。断言或要求输入吐回“ParserError: Expected ',' but got identifier assert (uint i = 0; i < _index + 1; i++) {"
我倾向于 require 实现,因为在合同开始时已经导入了安全的数学库。我见过许多具有相同实现的不同合同,但在确定正确方法时遇到了一些问题。非常感谢您的帮助。
算术运算可能会溢出。
可能会导致算术溢出。通过使用必需的 () 语句或用于整数算术运算的 OpenZeppelin SafeMath 库限制输入来防止溢出。参考为此问题生成的事务跟踪以重现溢出
我也会附上漏洞的复现
[重现此漏洞的说明(测试用例 1)]
contract FreezableToken is StandardToken {
// freezing chains
mapping (bytes32 => uint64) internal chains;
// freezing amounts for each chain
mapping (bytes32 => uint) internal freezings;
// total freezing balance per address
mapping (address => uint) internal freezingBalance;
event Freezed(address indexed to, uint64 release, uint amount);
event Released(address indexed owner, uint amount);
/**
* @dev Gets the balance of the specified address include freezing tokens.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return super.balanceOf(_owner) + freezingBalance[_owner];
}
/**
* @dev Gets the balance of the specified address without freezing tokens.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function actualBalanceOf(address _owner) public view returns (uint256 balance) {
return super.balanceOf(_owner);
}
function freezingBalanceOf(address _owner) public view returns (uint256 balance) {
return freezingBalance[_owner];
}
/**
* @dev gets freezing count
* @param _addr Address of freeze tokens owner.
*/
function freezingCount(address _addr) public view returns (uint count) {
uint64 release = chains[toKey(_addr, 0)];
while (release != 0) {
count++;
release = chains[toKey(_addr, release)];
}
}
/**
* @dev gets freezing end date and freezing balance for the freezing portion specified by index.
* @param _addr Address of freeze tokens owner.
* @param _index Freezing portion index. It ordered by release date descending.
*/
function getFreezing(address _addr, uint _index) public view returns (uint64 _release, uint _balance) {
for (uint i = 0; i < _index + 1; i++) { **<- Error Here ``< _index + 1; i++)``**
_release = chains[toKey(_addr, _release)];
if (_release == 0) {
return;
}
}
_balance = freezings[toKey(_addr, _release)];
}
/**
* @dev freeze your tokens to the specified address.
* Be careful, gas usage is not deterministic,
* and depends on how many freezes _to address already has.
* @param _to Address to which token will be freeze.
* @param _amount Amount of token to freeze.
* @param _until Release date, must be in future.
*/
function freezeTo(address _to, uint _amount, uint64 _until) public {
require(_to != address(0));
require(_amount <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender].sub(_amount);
bytes32 currentKey = toKey(_to, _until);
freezings[currentKey] = freezings[currentKey].add(_amount);
freezingBalance[_to] = freezingBalance[_to].add(_amount);
freeze(_to, _until);
emit Transfer(msg.sender, _to, _amount);
emit Freezed(_to, _until, _amount);
}
/**
* @dev release first available freezing tokens.
*/
function releaseOnce() public {
bytes32 headKey = toKey(msg.sender, 0);
uint64 head = chains[headKey];
require(head != 0);
require(uint64(block.timestamp) > head);
bytes32 currentKey = toKey(msg.sender, head);
uint64 next = chains[currentKey];
uint amount = freezings[currentKey];
balances[msg.sender] = balances[msg.sender].add(amount);
freezingBalance[msg.sender] = freezingBalance[msg.sender].sub(amount);
if (next == 0) {
} else {
chains[headKey] = next;
}
emit Released(msg.sender, amount);
}
/**
* @dev release all available for release freezing tokens. Gas usage is not deterministic!
* @return how many tokens was released
*/
function releaseAll() public returns (uint tokens) {
uint release;
uint balance;
(release, balance) = getFreezing(msg.sender, 0);
while (release != 0 && block.timestamp > release) {
releaseOnce();
tokens += balance;
(release, balance) = getFreezing(msg.sender, 0);
}
}
function toKey(address _addr, uint _release) internal pure returns (bytes32 result) {
// WISH masc to increase entropy
result = 0x5749534800000000000000000000000000000000000000000000000000000000;
assembly {
result := or(result, mul(_addr, 0x10000000000000000))
result := or(result, and(_release, 0xffffffffffffffff))
}
}
function freeze(address _to, uint64 _until) internal {
require (_until > block.timestamp);
bytes32 key = toKey(_to, _until);
bytes32 parentKey = toKey(_to, uint64(0));
uint64 next = chains[parentKey];
if (next == 0) {
chains[parentKey] = _until;
return;
}
bytes32 nextKey = toKey(_to, next);
uint parent;
while (next != 0 && _until > next) {
parent = next;
parentKey = nextKey;
next = chains[nextKey];
nextKey = toKey(_to, next);
}
if (_until == next) {
return;
}
if (next != 0) {
chains[key] = next;
}
chains[parentKey] = _until;
}
}```
[1]: https://i.stack.imgur.com/ayg2D.png
根据上下文,我假设错误消息“算术运算可能溢出。”来自静态分析工具。
假设您使用的是低于 0.8.0 的 Solidity 版本,for 循环定义在理论上容易受到整数溢出的影响。但前提是 _index
为 2^256,即 uint
的最大值。该值会使 _index + 1
表达式溢出。
仅导入 SafeMath 库是不够的。您还需要使用它的功能而不是原生算术运算来防止溢出。
contract FreezableToken is StandardToken {
// allows to use functions of the library on `uint` type
using SafeMath for uint;
function getFreezing() public {
// use the `add()` function of the library instead of the `+` operation
for (uint i = 0; i < _index.add(1); i = i.add(1)) {
或者升级到 Solidity 版本 0.8+,检查语言级别的溢出,这样您就不必使用 SafeMath 库了。