动态阵列堆栈损坏

时间:2018-02-13 06:24:22

标签: ethereum solidity

此合约似乎是一个在合约余额中提供1/16赔率的游戏。但是,在调试器中运行代码时,它看起来好像是' secretNumber'变量在被使用之前被覆盖。

pragma solidity ^0.4.19;

contract CryptoRoulette {

    uint256 private secretNumber;
    uint256 public lastPlayed;
    uint256 public betPrice = 0.1 ether;
    address public ownerAddr;

    struct Game {
        address player;
        uint256 number;
    }
    Game[] public gamesPlayed;

    function CryptoRoulette() public {
        ownerAddr = msg.sender;
        shuffle();
    }

    function shuffle() internal {
        // initialize secretNumber with a value between 0 and 15
        secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;
    }

    function play(uint256 number) payable public {
        require(msg.value >= betPrice && number < 16);

        Game game;
        game.player = msg.sender;
        game.number = number;
        gamesPlayed.push(game);

        if (number == secretNumber) {
            // win!
            msg.sender.transfer(this.balance);
        }

        shuffle();
        lastPlayed = now;
    }

    function kill() public {
        if (msg.sender == ownerAddr && now > lastPlayed + 1 days) {
            suicide(msg.sender);
        }
    }

    function() public payable { }
}

secretNumber的更新方式,应始终小于16 secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;

此调试器输出显示在执行if (number == secretNumber) {期间,secretNumber的值已经更新,奇怪的是,调用者地址(msg.sender)。

`
(243) PUSH1 0x00
  000000000000000000000000000000000000000000000000000000006898f82b
  0000000000000000000000000000000000000000000000000000000000000143
  0000000000000000000000000000000000000000000000000000000000000003
  0000000000000000000000000000000000000000000000000000000000000000 (top)

40:         if (number == secretNumber) {
                          ^^^^^^^^^^^^

debug(develop:0x98cacf83...)> i

CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:

40:         if (number == secretNumber) {
                ^^^^^^

debug(develop:0x98cacf83...)> p

CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:

(245) DUP3
  000000000000000000000000000000000000000000000000000000006898f82b
  0000000000000000000000000000000000000000000000000000000000000143
  0000000000000000000000000000000000000000000000000000000000000003
  0000000000000000000000000000000000000000000000000000000000000000
  000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57 (top)

40:         if (number == secretNumber) {
                ^^^^^^

我的猜测是条件之前的存储访问导致堆栈以某种方式被破坏。

这是一个已知的漏洞吗?有人可以解释一下发生了什么吗?

1 个答案:

答案 0 :(得分:1)

尝试创建本地引用时未指定正确的存储位置,这是一个常见问题。

来自Solidity docs

  

存储位置有默认值,具体取决于它所涉及的变量类型:

     
      
  • 状态变量始终存储
  •   
  • 函数参数默认在内存中
  •   
  • 默认情况下结构,数组或映射类型引用存储的局部变量
  •   
  • 值类型的局部变量(即,数组,结构和映射都不存储)存储在堆栈中
  •   

粗体注释表示行Game game;默认为存储。如果未初始化存储变量,则默认情况下将指向存储槽0。最终结果是当您对game(使用game.player = msg.sender;)进行更改时,它会将值写入第一个插槽,这将是合同中的第一个变量(在这种情况下) ,secretNumber)。

写这个的正确方法是使用Game memory game;。如果您出于这个原因省略memory关键字,编译器会给出警告。