动态数组堆栈损坏

Dynamic Array Stack Corruption

此合同似乎是一个在合同余额中提供 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) {
                ^^^^^^

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

这是已知漏洞吗?有人可以解释一下这是怎么回事吗?

这是在未指定正确存储位置的情况下尝试创建本地引用时的常见问题。

来自Solidity docs

There are defaults for the storage location depending on which type of variable it concerns:

  • state variables are always in storage
  • function arguments are in memory by default
  • local variables of struct, array or mapping type reference storage by default
  • local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack

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

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