true_positive: yes
fp_type: 2
source: yes
This is the Spankchain incident. See the writeup of the Spankchain project for more details about the incident.
The variable Channels[_lcID]
is a struct of type Channel
, which due to size occupies various storage slots.
Sereum will lock the part of the struct, which are used in conditional checks.
The first require
statement will lock the first storage address occupied by the Channels[_lcID]
variable.
The last statement in LCOpenTimeout
is a delete
statement, which is implemented on the EVM level as SSTORE
instructions with value 0.
Storage slots with value 0 are not explicitely stored on the blockchain (as all storage is implicitely initialized with 0).
function LCOpenTimeout(bytes32 _lcID) public {
/// Sereum: write-lock first storage word of Channels[_lcID]
require(msg.sender == Channels[_lcID].partyAddresses[0] && Channels[_lcID].isOpen == false);
require(now > Channels[_lcID].LCopenTimeout);
if(Channels[_lcID].initialDeposit[0] != 0) {
Channels[_lcID].partyAddresses[0].transfer(Channels[_lcID].ethBalances[0]);
}
if(Channels[_lcID].initialDeposit[1] != 0) {
/// Sereum: external call here
/// Note: Channels[_lcID].token is a potentially attacker controlled contract.
/// and transfer here is not the solidity method, but the transfer method of
/// the target contract.
require(Channels[_lcID].token.transfer(Channels[_lcID].partyAddresses[0], Channels[_lcID].erc20Balances[0]),"CreateChannel: token transfer failure");
}
emit DidLCClose(_lcID, 0, Channels[_lcID].ethBalances[0], Channels[_lcID].erc20Balances[0], 0, 0);
// only safe to delete since no action was taken on this channel
/// Sereum: write to locked variable (Channels[_lcID]) with SSTORE(Channels[_lcID],0)
delete Channels[_lcID];
}