前言
- 关于智能合约solidity语法,请点击参考资料
- 本文摘取的是的BNB合约源码,如需查看源码,可点击[Code]
(https://etherscan.io/address/0xb8c77482e45f1f44de1745f52c74426c631bdd52#code)
浅谈 BEC的合约漏洞
- 查阅BEC的智能合约代码,BEC的合约漏洞是batchTransfer函数的数据溢出。BEC 直接使用普通的加减乘除符号,缺少溢出判断,这就造成数据溢出的隐患。
uint256 amount = uint256(cnt) * _value;
如果cnt = 2, _value=2^255
,那么amount = 0
。因为溢出后,计算机取后面256
位(都是0
)。
//合约地址:https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d#code
//合约代码
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value;
require(cnt > 0 && cnt <= 20);
require(_value > 0 && balances[msg.sender] >= amount);
balances[msg.sender] = balances[msg.sender].sub(amount);
for (uint i = 0; i < cnt; i++) {
balances[_receivers[i]] = balances[_receivers[i]].add(_value);
Transfer(msg.sender, _receivers[i], _value);
}
return true;
}
分析BNB 的 SafeMath源码
乘法,仅限内部调用,返回
uint256
uint256 c = a * b;
容易溢出,比如a=2,b=2^255
乘积2^256
刚好溢出。结果取后面的256位(全为0),导致c=0
。- 所以使用
(a == 0 || c / a == b)
,验证结果的一致性
function safeMul(uint256 a, uint256 b) internal returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
除法,仅限内部调用,返回
uint256
assert(b > 0)
, 确保被除数不能为0
assert(a == b * c + a % b);
防止溢出,验证结果的一致性
function safeDiv(uint256 a, uint256 b) internal returns (uint256) {
assert(b > 0);
uint256 c = a / b;
assert(a == b * c + a % b);
return c;
}
减法,仅限内部调用,返回
uint256
assert(b <= a)
因为返回值需要是 正数,所以此处判断b
必须小于等于a
function safeSub(uint256 a, uint256 b) internal returns (uint256) {
assert(b <= a);
return a - b;
}
加法,仅限内部调用,返回 uint256
assert(c>=a && c>=b);
//验证结果: 两个正数相加,和一定大于每个加数
function safeAdd(uint256 a, uint256 b) internal returns (uint256) {
uint256 c = a + b;
assert(c>=a && c>=b);
return c;
}
总结:
不要直接使用简单的 "+-*/" ,尽量使用 library SafeMath 中的函数,避免整数溢出的隐患。
关于计算机处理乘除法的原理
请参考:https://www.cnblogs.com/mamamia/p/7760341.html
网友评论