Home Blockchain Solidity漏洞 未检查的call的返回值
Post
Cancel

Blockchain Solidity漏洞 未检查的call的返回值

在Solidity中,有很多方法可以执行外部调用,将以太币传送到外部帐户通常是通过transfer()方法进行的。然而,send()函数也可以使用,并且对于更多用途的外部调用,CALL操作码可以直接用于Solidity中。call()和send()函数返回一个布尔值来表示调用是成功还是失败。

因此,这些函数有一个简单的警告,即如果外部调用失败(初始化call()或send()失败,而不是call()或send()返回false),则执行这些函数的交易将不会恢复。当返回值没有被检查时,会出现一个常见的陷阱,而开发者则预期会出现一个复原。

坑点分析

考虑下面的例子: image

代码15

代码15代表了一个lotto式的合约,winner可以获得数量为winAmount的以太币,这些以太币通常会留下一些让任何人都能取回的余地。

这一问题存在于在没有检查响应的情况下使用send()函数的地方。在这个例子中,如果一个winner的交易失败(要么因为耗尽了gas,这是一个出让函数在合约中故意抛出的错误,要么堆栈调用的深度攻击),允许payedOut设置为true(不管以太币是否被发送)。

在这种情况下,公众可以通过withdrawLeftOver()函数提取winner的赏金。

避坑技巧

在可能的情况下,如果外部交易复原,则使用transfer()函数而不是send()函数作为复原的方式。如果需要send(),需要始终确保对返回值的检查。

当然,更好的方式是采用withdrawal模式,这个模式中每个用户都需要调用一个独立的函数(例如 withdraw函数)来处理从合约中发送以太币的问题,因此独立处理发送交易失败的结果。

这个想法是从逻辑上将外部发送函数从代码基础的其余部分分离出来,并将可能失败交易的负担放到了调用withdraw函数的最终用户身上。

真实案例:Etherpot和King of the Ether

Etherpot是一个彩票的智能合约,与上面例子中提到的合约没有太大的不同。如下面代码所示: image

这个合约的问题在于,不正确地使用了区块哈希。然而,这个合约也受到了没有检查调用返回值的影响。

值得留意的是,在第21行中,发送函数的返回值没有被检查,然后下一行设置了一个布尔值,表示获胜者已经收到了他们的赏金。这个缺陷可以让一个赢家不能得到以太币,但是合约的状态可以表明赢家已经收到了钱。

This post is licensed under CC BY 4.0 by the author.