这篇博客文章是该系列文章的第二篇,将报告一些简朴的现实中智能合约安详性Bug,黑客们是如何操作它们造成系统的影响以及提供相应的修复代码。到今朝为止,我们已经实现了3,000万美元的修复挽救,即直接归因于智能合约安详裂痕的2.5亿美元的损失。这次我们将别离存入两笔存款,别离为:57,896,044,618,658,097,711,785,492,504,343,953,926,634,992,332,820,282,019,728,792,003,956,564,819,968个代币,总计0个代币!
追念起来很简朴吗?回看一下已往Bug,险些所有安详Bug都变得很是明明,但二十多年来,开拓人员一直在犯这些沟通的错误。请留意,上述修复代码对付BeautyChainToken来说是没有意义的,因为智能合约的软件没有更新。一旦陈设了智能合约,它们除原始代码之外就根基上不行变动且不行否决。因此,我们很是简朴的办理方案(仅由几个字符构成)只能成为将来的一个教导,强调了“不要让算术功效堕落,然后继承利用错误数据执行”。
256 uint cnt = _receivers.length;
Solidity中最常见的数据范例之一是unsign256位整数(uint256或uint),它可以暗示0到2^256-1之间的任何值。假如合约代码执行一个普通的算术运算,获得的功效超出了这个范畴,则功效会暗暗地被包装起来,合约也将会继承利用错误的数据,这长短常可骇的功效。出于这个原因,上面显示的合约出格是利用了前面看到的safemath库中的专用的.sub()和.add()函数(别离位于第261行和第263行)。假如碰着下溢或上溢,将编写这些通用函数来中止操纵,这将阻止执行继承处理惩罚不良数据。您大概会问为什么第257行利用普通的’*’而不是SafeMath中的.mul()。
– uint256 amount = uint256(cnt) * _value;
+ uint256 amount = uint256(cnt).mul(_value);
258 require(cnt > 0 && cnt <= 20);
259 require(_value > 0 && balances[msg.sender] >= amount);
Bug!关于第257行,假如_value配置为2^255,然后由于在前一行计较出的cnt为2而更加,则由于回绕(wrap-around)效应,功效将被存储进amount为0(而不是2^256)。这是我们旨在办理简直切错误-此刻将继承处理惩罚错误数据,继承执行。要求的_value很大,但总计较amount为0!amount为0金额时余额查抄将会被忽略第259行,并继承通过第261行上的举办余额调。然而在执行单个传输的轮回操作了庞大的_value变量(不再引用数量变量)。这意味着轮回将使两个方针余额别离增加2^255。累积增加!
265 }
255 function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
261 balances[msg.sender] = balances[msg.sender].sub(amount);
257 uint256 amount = uint256(cnt) * _value;
262 for (uint i = 0; i < cnt; i++) {
让我们从第255行开始查抄batchTransfer()函数。它有两个参数-第一个参数(_receivers)是方针帐户地点的列表,第二个参数(_value)是要转移到每个方针帐户地点的代币数。第256行计较列表中吸收方地点的数量,然后第257行可以计较从发送方帐户余额中提取的总金额。第259行确保发送方有足够的余额,然后第261将第257行向下调解之前的总金额。最后第262行开始轮回,通过将每个吸收者的余额向上调解_value来执行单独的转帐。
这篇博客文章将专门先容算术运算的Bug(譬喻整数溢出)尽量智能合约提供了具有奇特新颖性的执行情况,但这无疑不是一个新问题。算法的Bug险些占据了SEI CERT Oracle Java编码尺度中大部门的吸引力。其他开拓语言也蒙受完全沟通的可骇环境,裂痕映射到几个常见的Bug中。智能合约主要面对与民众执行情况的安详,不行改观的性质,差异的好处相关者种别以及逻辑巨大性相关的奇特挑战。
260
267 }
让我们看一下BeautyChainToken的合约代码,该代码可以在Etherscan上可以轻松找到。通过欣赏代码可以发此刻最顶部四周有一个名为SafeMath的库的实现,随后是一系列彼此依存的合约。忽略周围其他的代码滋扰,但请留意常常引用变量to,from,amount和balances以及称为transfer()和approve()的函数,这是一个不到300行的全成果代码。
让我们修复它。我们要做的就是用.mul()替换第257行上的普通“ *”,如下所示。前缀为“ –”的行将从合约代码中删除,而前缀为“ +”的行将被添加。
263 balances[_receivers[i]] = balances[_receivers[i]].add(_value);
没那么简朴。代币是智能合约的一个用例之一,都是由顶级专家举办很是审慎的开拓。SafeMath库的存在表白这是一个已知的问题区域,可是我们上面看到的代码确实利用了SafeMath库。实际上,ERC-20代币尺度自己就有几个已知的问题,这些问题仅能部门办理(最好)。
产生了什么?进攻者挪用了上面所示的batchTransfer()函数,并为其提供了一个_receivers列表,该列表包括两个地点和一个_value参数配置为2^255。在Etherscan事务中可以清楚地看到这一点(单击“解码输入数据”)。如前所述,地点的cnt计较为2,因此乘以2^255的值将导致溢出到0。此错误数据将继承执行。通过各类参数查抄,发件人的余额淘汰到0,最后将_value的两笔存款存入_receivers余额。顺便说一句,假如您还没有猜到的话,本帖子第一段中提到的大量数字刚好是2^255。约莫0.02美元的真金白银来扶助进攻生意业务,两笔大量的代币是凭空建设的,并存放到_receiver地点中。这导致了对该代币合约的信任丧失,从而彻底粉碎了其代价。
264 Transfer(msg.sender, _receivers[i], _value);
,
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。