当前位置:   article > 正文

Solidity Integer Overflow and Underflow

Solidity Integer Overflow and Underflow

前言

实际上整形溢出并不仅仅是在智能合约中出现问题,在其他的地方也有出现。只不过我感觉我好像没咋遇到过。。。还是我太菜了。。
学习参考:
underflow-overflow
Integer Overflow and Underflow

Integer Overflow

整形溢出其实还是比较常见的,尤其是编程入门学的基本就是C语言,C语言就有这样的问题。但是自己主要还是一直在接触PHP和Python,因此对于整形溢出就没那么敏感了。

一个简单的例子:

pragma solidity <=0.9.0;

contract test1 {
    uint public number=2**256-1;
    function add(uint num) public {
        number+=num;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

uint即uint256,最大的位数是256位,所能表示的最大的整数是2^256-1。那么如果在已经表示最大的情况下又加了1呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
成功出现了溢出,导致uint256从2^256+1溢出到了0。
原理非常的简单,那么具体该怎么利用呢?看一下ctfwiki的这个例子:

pragma solidity ^0.4.21;

contract TokenSaleChallenge {
    mapping(address => uint256) public balanceOf;
    uint256 constant PRICE_PER_TOKEN = 1 ether;

    function TokenSaleChallenge(address _player) public payable {
        require(msg.value == 1 ether);
    }

    function isComplete() public view returns (bool) {
        return address(this).balance < 1 ether;
    }

    function buy(uint256 numTokens) public payable {
        require(msg.value == numTokens * PRICE_PER_TOKEN);

        balanceOf[msg.sender] += numTokens;
    }

    function sell(uint256 numTokens) public {
        require(balanceOf[msg.sender] >= numTokens);

        balanceOf[msg.sender] -= numTokens;
        msg.sender.transfer(numTokens * PRICE_PER_TOKEN);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

注意这里的PRICE_PER_TOKEN = 1 ether,单位换算如下:

assert(1 wei == 1);
assert(1 gwei == 1e9);
assert(1 ether == 1e18);
  • 1
  • 2
  • 3

注意buy的函数:

require(msg.value == numTokens * PRICE_PER_TOKEN);
  • 1

虽然这里的单位是1 ether,但是要知道它前面的类型是uint256,本地试试:

uint public price = 1 ether;
  • 1

在这里插入图片描述
因此就会发现,这里的numTokens * PRICE_PER_TOKEN存在溢出问题。要么要怎么溢出呢?这是在buy函数里,msg.value是随消息发送的 wei 的数量。因此这里是要花最少的前,买更多的Token。因此这里的numTokens取 2**256//1**18+1,这样就可以成功以低价买到超过的Tokens,然后再sell出去,很nice。

Integer Underflow

整形下溢出就同理了,之前的重入攻击也已经遇到过了,也确实这个下溢出经常配合重入攻击来使用。
看一下ctfwiki的例子:

contract Bank {
    mapping(address => uint256) public balanceOf;
    ...
    function withdraw(uint256 amount) public {
        require(balanceOf[msg.sender] - amount >= 0);
        balanceOf[msg.sender] -= amount;
        msg.sender.send.value(amount)();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这个例子我一开始也没看出来到底有什么问题,主要还是php和python的那种数据没有大小限制和类型限制的那种思想太根深蒂固了。

关键在于这里:balanceOf[msg.sender] - amount >= 0
要知道二者都是uint类型,因此无论怎么减,得到的肯定也还是uint,因此一定是>=0的,只不过会发生下溢出。因此这里其实就是无限取款了。
正确的写法:

require(balanceOf[msg.sender] >= amount)
  • 1

看来还是要多注意这些,感觉自己特别容易写出这种有问题的代码。。。

防御

整数溢出漏洞可以使用 SafeMath 库来防御,当发生溢出时会回滚交易。

总结

学习了一波Integer Overflow and Underflow,接下来再去刷2个题目加深一下印象。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/141919
推荐阅读
相关标签
  

闽ICP备14008679号