此合约似乎是一个在合约余额中提供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) {
^^^^^^
我的猜测是条件之前的存储访问导致堆栈以某种方式被破坏。
这是一个已知的漏洞吗?有人可以解释一下发生了什么吗?
答案 0 :(得分:1)
尝试创建本地引用时未指定正确的存储位置,这是一个常见问题。
存储位置有默认值,具体取决于它所涉及的变量类型:
- 状态变量始终存储
- 函数参数默认在内存中
- 默认情况下结构,数组或映射类型引用存储的局部变量
- 值类型的局部变量(即,数组,结构和映射都不存储)存储在堆栈中
粗体注释表示行Game game;
默认为存储。如果未初始化存储变量,则默认情况下将指向存储槽0。最终结果是当您对game
(使用game.player = msg.sender;
)进行更改时,它会将值写入第一个插槽,这将是合同中的第一个变量(在这种情况下) ,secretNumber
)。
写这个的正确方法是使用Game memory game;
。如果您出于这个原因省略memory
关键字,编译器会给出警告。