代码之家  ›  专栏  ›  技术社区  ›  proofskiddie

动态数组堆栈损坏

  •  2
  • proofskiddie  · 技术社区  · 7 年前

    这份合同似乎是一个在合同余额中提供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 回复  |  直到 7 年前
        1
  •  1
  •   Community CDub    4 年前

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

    Solidity docs :

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

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

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

    写这篇文章的正确方法是 Game memory game; . 如果省略 memory 正是因为这个原因。