-
Notifications
You must be signed in to change notification settings - Fork 20.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crash in gasSStoreEIP2200 due to panic in SubRefund #20119
Comments
I observed the same crash when fuzzing this slight simpler contract:
This is the corresponding (optimized) bytecode:
|
I've tried to repro this on a local dev chain, but couldn't:
|
Hmm, the gas used is funky. Are you sure your byte code mathes the solidity file? Either way, I did redeploy the bytecode from my compilation via remix and the gas used for Incr seems better:
Still no crash though. |
Perhaps you are running a more complex suite with contract interactions? Could you share a repro that we could run standalone? |
@karalabe Thanks a lot for looking into this so quickly. It took me a while to reproduce it, but here's the resulting code:
It seems like the |
Ah, I think I see what the issue is. When you call However, afterwards you do a SetState, but you do not Finalize/IntermediateRoot/Commit it, rather execute a transaction on top. This would be equivalent to one transaction getting suspended halfway through and a second one executed on top. You are breaking transaction boundary invariants. In EIP2200, your refund depends on whether a storage slot was dirty or not (if it was dirty, you get back more gas for restoring it's original value). The refund counter during normal operation cannot go into negative numbers, because the operation that makes a slot dirty in the first place costs a lot of gas. In your code above however, you made the slot dirty "out of bounds" by calling Here's your test case, cleaned up a bit (I put it into func TestRefund(t *testing.T) {
ctx := vm.Context{
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: func(n uint64) common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
},
GasPrice: new(big.Int),
GasLimit: 0x27100,
BlockNumber: hexutil.MustDecodeBig("0x661a55"),
}
contract := common.HexToAddress("0x0901d12ebe1b195e5aa8748e62bd7734ae19b51f")
sdb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
sdb.SetCode(contract, hexutil.MustDecode("0x6080604052348015600f57600080fd5b506004361060285760003560e01c806343c9250414602d575b600080fd5b60336035565b005b60008054600101905556fea265627a7a723158204ab640601aa22be0d859e025aa75f73ee1738552892def5cfef1eaaaa49a8f3f64736f6c634300050b0032"))
sdb.SetState(contract, common.HexToHash("0x0"), common.HexToHash("0x1"))
sdb.Commit(false)
sdb.SetState(contract, common.HexToHash("0x0"), common.HexToHash("0x0"))
sdb.Finalise(false) // Make sure the transaction after this point starts with a clean slate
var (
evm = vm.NewEVM(ctx, sdb, params.AllEthashProtocolChanges, vm.Config{})
data = hexutil.MustDecode("0x43c9250400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
msg = types.NewMessage(common.Address{}, &contract, 0, big.NewInt(0), 0x27100, new(big.Int), data, false)
)
ApplyMessage(evm, msg, new(GasPool).AddGas(0x7d0000))
} This does not crash, because I added an As to why crash and not error out. Because it's a broken invariant. It cannot happen that the refund counter goes into negative. If it does, there's a programming error somewhere and it must be investigated and remedied immediately. |
@karalabe Thanks again for looking into this and the detailed explanation! I wasn't aware of that invariant, but that makes sense and also explains why the issue only surfaced after I activated the Istanbul changes. I assume that after |
This "side effect" where slots get dirtied was introduced for Istanbul (actually, for Constantinople too, but reverted by Petersburg). That's why you've hit it only now, because before Istanbul, there was no notion of different gas pricing based on the contents of the slot. |
@karalabe I came across another such crash for the following piece of code:
Any idea why that might happen? It seems like all state DBs are finalized before applying the transactions. |
`sdb2` is not finalized. You do `applyMessage` on it, then copy it to a new sdb without finalizing
|
Though, if sdb3 is then finalized, should it matter? Could it be that you reuse the same gasPool instance in 2 transaction invocations? |
@holiman I just tried adding |
@karalabe @holiman I just tried to simply the code some more: // We set up the initial state.
db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db))
to := common.HexToAddress("0xaaa1")
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("60606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e3391ec1461009957806313678f70146100eb5780638db0cb2114610121578063955e136f1461017b578063a035b1fe146101cd578063a7695848146101f3578063cfe25be01461028c578063f2853292146102c2578063fb92488b146102f8575bfe5b34156100a157fe5b6100a9610318565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100f357fe5b61011f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061033e565b005b341561012957fe5b610179600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b7565b005b6101cb600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610550565b005b34156101d557fe5b6101dd61069a565b6040518082815260200191505060405180910390f35b34156101fb57fe5b6102036106a0565b6040518080602001828103825283818151815260200191508051906020019080838360008314610252575b8051825260208311156102525760208201915060208101905060208303925061022e565b505050905090810190601f16801561027e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561029457fe5b6102c0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073e565b005b34156102ca57fe5b6102f6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107e7565b005b341561030057fe5b6103166004808035906020019091905050610889565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561039b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16ff5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16313373ffffffffffffffffffffffffffffffffffffffff16311115156104165760006000fd5b806000908051906020019061042c92919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360008314610512575b805182526020831115610512576020820191506020810190506020830392506104ee565b505050905090810190601f16801561053e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b6002543410156105605760006000fd5b806000908051906020019061057692919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528381815181526020019150805190602001908083836000831461065c575b80518252602083111561065c57602082019150602081019050602083039250610638565b505050905090810190601f1680156106885780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b60025481565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107365780601f1061070b57610100808354040283529160200191610736565b820191906000526020600020905b81548152906001019060200180831161071957829003601f168201915b505050505081565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561079b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc670de0b6b3a76400009081150290604051809050600060405180830381858888f1935050505015156107e357fe5b5b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108445760006000fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108e65760006000fd5b806002819055507fa6dc15bdb68da224c66db4b3838d9a2b205138e8cff6774e57d0af91e196d6226002546040518082815260200191505060405180910390a15b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061096b57805160ff1916838001178555610999565b82800160010185558215610999579182015b8281111561099857825182559160200191906001019061097d565b5b5090506109a691906109aa565b5090565b6109cc91905b808211156109c85760008160009055506001016109b0565b5090565b905600a165627a7a72305820898aa45f7eb811ccffb977046caf5344261459d9dfea6db4d4441ccfa68b86ea0029")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x1"))
o := common.HexToAddress("0xaaa2")
bal, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb.SetBalance(o, bal)
sdb.Finalise(false)
sdb2 := sdb.Copy()
// We run the first transaction.
getHash := func(n uint64) common.Hash {
return common.Hash{}
}
ctx := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasPrice: big.NewInt(0),
Origin: o,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: big.NewInt(0),
}
chainConfig := ¶ms.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
evm := vm.NewEVM(ctx, sdb2, chainConfig, vm.Config{})
data, _ := hex.DecodeString("955e136f")
toCopy := common.BytesToAddress(to.Bytes())
msg := types.NewMessage(o, &toCopy, 0, big.NewInt(0xffffffffffffff), 0x7d000, big.NewInt(0), data, false)
_, _, _, appErr := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr1: %v\n", appErr)
// "Fix 1": uncommenting the following line.
// sdb2.Finalise(false)
sdb3 := sdb2.Copy()
// sdb3.Finalise(false)
o2 := common.HexToAddress("0xaaa3")
bal2, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb3.SetBalance(o2, bal2)
// "Fix 2": using "Commit" instead of "Finalize" in the following line.
sdb3.Finalise(false)
sdb4 := sdb3.Copy()
// We run the second transaction which crashes.
ctx2 := vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasPrice: big.NewInt(0),
Origin: o2,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: big.NewInt(0),
}
evm2 := vm.NewEVM(ctx2, sdb4, chainConfig, vm.Config{})
data2, _ := hex.DecodeString("8db0cb21000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000129")
toCopy2 := common.BytesToAddress(to.Bytes())
msg2 := types.NewMessage(o2, &toCopy2, 0, big.NewInt(0), 0x7d000, big.NewInt(0), data2, false)
_, _, _, appErr2 := core.ApplyMessage(evm2, msg2, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr2: %v\n", appErr2) While doing so, I also discovered another "fix" (see comment "fix 2"): using |
I ported the above code to the latest go-ethereum version in case someone wants to reproduce this: // We set up the initial state.
db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db), nil)
to := common.HexToAddress("0xaaa1")
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("60606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e3391ec1461009957806313678f70146100eb5780638db0cb2114610121578063955e136f1461017b578063a035b1fe146101cd578063a7695848146101f3578063cfe25be01461028c578063f2853292146102c2578063fb92488b146102f8575bfe5b34156100a157fe5b6100a9610318565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100f357fe5b61011f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061033e565b005b341561012957fe5b610179600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b7565b005b6101cb600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610550565b005b34156101d557fe5b6101dd61069a565b6040518082815260200191505060405180910390f35b34156101fb57fe5b6102036106a0565b6040518080602001828103825283818151815260200191508051906020019080838360008314610252575b8051825260208311156102525760208201915060208101905060208303925061022e565b505050905090810190601f16801561027e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561029457fe5b6102c0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073e565b005b34156102ca57fe5b6102f6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107e7565b005b341561030057fe5b6103166004808035906020019091905050610889565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561039b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16ff5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16313373ffffffffffffffffffffffffffffffffffffffff16311115156104165760006000fd5b806000908051906020019061042c92919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360008314610512575b805182526020831115610512576020820191506020810190506020830392506104ee565b505050905090810190601f16801561053e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b6002543410156105605760006000fd5b806000908051906020019061057692919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528381815181526020019150805190602001908083836000831461065c575b80518252602083111561065c57602082019150602081019050602083039250610638565b505050905090810190601f1680156106885780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b60025481565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107365780601f1061070b57610100808354040283529160200191610736565b820191906000526020600020905b81548152906001019060200180831161071957829003601f168201915b505050505081565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561079b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc670de0b6b3a76400009081150290604051809050600060405180830381858888f1935050505015156107e357fe5b5b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108445760006000fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108e65760006000fd5b806002819055507fa6dc15bdb68da224c66db4b3838d9a2b205138e8cff6774e57d0af91e196d6226002546040518082815260200191505060405180910390a15b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061096b57805160ff1916838001178555610999565b82800160010185558215610999579182015b8281111561099857825182559160200191906001019061097d565b5b5090506109a691906109aa565b5090565b6109cc91905b808211156109c85760008160009055506001016109b0565b5090565b905600a165627a7a72305820898aa45f7eb811ccffb977046caf5344261459d9dfea6db4d4441ccfa68b86ea0029")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x1"))
o := common.HexToAddress("0xaaa2")
bal, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb.SetBalance(o, bal)
sdb.Finalise(false)
sdb2 := sdb.Copy()
// We run the first transaction.
getHash := func(n uint64) common.Hash {
return common.Hash{}
}
txCtx := vm.TxContext{
Origin: o,
GasPrice: big.NewInt(0),
}
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: big.NewInt(0),
BaseFee: big.NewInt(0),
}
chainConfig := ¶ms.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
evm := vm.NewEVM(blockCtx, txCtx, sdb2, chainConfig, vm.Config{})
data, _ := hex.DecodeString("955e136f")
toCopy := common.BytesToAddress(to.Bytes())
msg := types.NewMessage(o, &toCopy, 0, big.NewInt(0xffffffffffffff), 0x7d000, big.NewInt(0), big.NewInt(0), big.NewInt(0), data, nil, false)
_, appErr := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr1: %v\n", appErr)
// "Fix 1": uncommenting the following line.
// sdb2.Finalise(false)
sdb3 := sdb2.Copy()
// sdb3.Finalise(false)
o2 := common.HexToAddress("0xaaa3")
bal2, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb3.SetBalance(o2, bal2)
// "Fix 2": using "Commit" instead of "Finalize" in the following line.
sdb3.Finalise(false)
sdb4 := sdb3.Copy()
// We run the second transaction which crashes.
txCtx2 := vm.TxContext{
Origin: o2,
GasPrice: big.NewInt(0),
}
blockCtx2 := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: big.NewInt(0),
BaseFee: big.NewInt(0),
}
evm2 := vm.NewEVM(blockCtx2, txCtx2, sdb4, chainConfig, vm.Config{})
data2, _ := hex.DecodeString("8db0cb21000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000129")
toCopy2 := common.BytesToAddress(to.Bytes())
msg2 := types.NewMessage(o2, &toCopy2, 0, big.NewInt(0), 0x7d000, big.NewInt(0), big.NewInt(0), big.NewInt(0), data2, nil, false)
_, appErr2 := core.ApplyMessage(evm2, msg2, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr2: %v\n", appErr2) |
I ported the above code to the latest go-ethereum version in case someone wants to reproduce this: // We set up the initial state.
db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db), nil)
to := common.HexToAddress("0xaaa1")
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("60606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e3391ec1461009957806313678f70146100eb5780638db0cb2114610121578063955e136f1461017b578063a035b1fe146101cd578063a7695848146101f3578063cfe25be01461028c578063f2853292146102c2578063fb92488b146102f8575bfe5b34156100a157fe5b6100a9610318565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100f357fe5b61011f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061033e565b005b341561012957fe5b610179600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b7565b005b6101cb600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610550565b005b34156101d557fe5b6101dd61069a565b6040518082815260200191505060405180910390f35b34156101fb57fe5b6102036106a0565b6040518080602001828103825283818151815260200191508051906020019080838360008314610252575b8051825260208311156102525760208201915060208101905060208303925061022e565b505050905090810190601f16801561027e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561029457fe5b6102c0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073e565b005b34156102ca57fe5b6102f6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107e7565b005b341561030057fe5b6103166004808035906020019091905050610889565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561039b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16ff5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16313373ffffffffffffffffffffffffffffffffffffffff16311115156104165760006000fd5b806000908051906020019061042c92919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360008314610512575b805182526020831115610512576020820191506020810190506020830392506104ee565b505050905090810190601f16801561053e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b6002543410156105605760006000fd5b806000908051906020019061057692919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528381815181526020019150805190602001908083836000831461065c575b80518252602083111561065c57602082019150602081019050602083039250610638565b505050905090810190601f1680156106885780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b60025481565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107365780601f1061070b57610100808354040283529160200191610736565b820191906000526020600020905b81548152906001019060200180831161071957829003601f168201915b505050505081565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561079b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc670de0b6b3a76400009081150290604051809050600060405180830381858888f1935050505015156107e357fe5b5b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108445760006000fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108e65760006000fd5b806002819055507fa6dc15bdb68da224c66db4b3838d9a2b205138e8cff6774e57d0af91e196d6226002546040518082815260200191505060405180910390a15b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061096b57805160ff1916838001178555610999565b82800160010185558215610999579182015b8281111561099857825182559160200191906001019061097d565b5b5090506109a691906109aa565b5090565b6109cc91905b808211156109c85760008160009055506001016109b0565b5090565b905600a165627a7a72305820898aa45f7eb811ccffb977046caf5344261459d9dfea6db4d4441ccfa68b86ea0029")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x1"))
o := common.HexToAddress("0xaaa2")
bal, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb.SetBalance(o, bal)
sdb.Finalise(false)
sdb2 := sdb.Copy()
// We run the first transaction.
getHash := func(n uint64) common.Hash {
return common.Hash{}
}
txCtx := vm.TxContext{
Origin: o,
GasPrice: big.NewInt(0),
}
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: 0,
BaseFee: big.NewInt(0),
}
newUint64 := func(val uint64) *uint64 { return &val }
chainConfig := ¶ms.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
ShanghaiTime: newUint64(0),
Ethash: new(params.EthashConfig),
}
evm := vm.NewEVM(blockCtx, txCtx, sdb2, chainConfig, vm.Config{})
data, _ := hex.DecodeString("955e136f")
toCopy := common.BytesToAddress(to.Bytes())
msg := &core.Message{
To: &toCopy,
From: o,
Nonce: 0,
Value: big.NewInt(0xffffffffffffff),
GasLimit: 0x7d000,
GasPrice: big.NewInt(0),
GasFeeCap: big.NewInt(0),
GasTipCap: big.NewInt(0),
Data: data,
AccessList: nil,
SkipAccountChecks: true,
}
_, appErr := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr1: %v\n", appErr)
// "Fix 1": uncommenting the following line.
// sdb2.Finalise(false)
sdb3 := sdb2.Copy()
o2 := common.HexToAddress("0xaaa3")
bal2, _ := math.ParseBig256("0xfffffffffffffffffffffff")
sdb3.SetBalance(o2, bal2)
// "Fix 2": using "Commit" instead of "Finalize" in the following line.
sdb3.Finalise(false)
sdb4 := sdb3.Copy()
// We run the second transaction which crashes.
txCtx2 := vm.TxContext{
Origin: o2,
GasPrice: big.NewInt(0),
}
blockCtx2 := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: 0,
BaseFee: big.NewInt(0),
}
evm2 := vm.NewEVM(blockCtx2, txCtx2, sdb4, chainConfig, vm.Config{})
data2, _ := hex.DecodeString("8db0cb21000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000129")
toCopy2 := common.BytesToAddress(to.Bytes())
msg2 := &core.Message{
To: &toCopy2,
From: o2,
Nonce: 0,
Value: big.NewInt(0),
GasLimit: 0x7d000,
GasPrice: big.NewInt(0),
GasFeeCap: big.NewInt(0),
GasTipCap: big.NewInt(0),
Data: data2,
AccessList: nil,
SkipAccountChecks: true,
}
_, appErr2 := core.ApplyMessage(evm2, msg2, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr2: %v\n", appErr2) |
We've investigated this today. Found the issue, though unsure yet how to fix it. The Copy method loses some dirtyness tracking when called on a dirty statedb, so a Finalize won't pick it up (optimization to only iterate over dirty items). Calling Commit however does pick it up and fix it (or IntermediateRoot too). We're a bit pondering what direction to take the fix. We could require Copy to run on only Finalized statedb (that's what we do and its a lot cleaner). We could have Copy implicitly call Finalize, but that feels surprising (still, it does somewhat do that even now). Or we could make Copy retain more dirty tracking information (can get weird). |
@karalabe Thanks a lot for taking another look! 🙏 Out of the three options, I like the second one the least (implicity calling Some time ago, I also ran into another issue (#28176) that may be related. Do you know if the other issue would also be fixed by the change you're considering? |
@karalabe @rjl493456442 Do you have an update on this issue? I noticed that the linked PR is still open. Did you decide to opt for a different fix? |
@holiman Thanks a lot for the update! Will these changes implicitly implement the the third option from above (retaining more dirty tracking in |
With the merging of #29520, a state and the copy of the state should be identical: because now we also make a copy of the journal, instead of just mashing the journal into the dirties. I'll play around with this a bit, but I think it can be closed |
Example updated to latest code, package main
import (
"encoding/hex"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
func main() {
// We set up the initial state.
db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db), nil)
to := common.HexToAddress("0xaaa1")
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("60606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e3391ec1461009957806313678f70146100eb5780638db0cb2114610121578063955e136f1461017b578063a035b1fe146101cd578063a7695848146101f3578063cfe25be01461028c578063f2853292146102c2578063fb92488b146102f8575bfe5b34156100a157fe5b6100a9610318565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100f357fe5b61011f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061033e565b005b341561012957fe5b610179600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b7565b005b6101cb600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610550565b005b34156101d557fe5b6101dd61069a565b6040518082815260200191505060405180910390f35b34156101fb57fe5b6102036106a0565b6040518080602001828103825283818151815260200191508051906020019080838360008314610252575b8051825260208311156102525760208201915060208101905060208303925061022e565b505050905090810190601f16801561027e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561029457fe5b6102c0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073e565b005b34156102ca57fe5b6102f6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107e7565b005b341561030057fe5b6103166004808035906020019091905050610889565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561039b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16ff5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16313373ffffffffffffffffffffffffffffffffffffffff16311115156104165760006000fd5b806000908051906020019061042c92919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360008314610512575b805182526020831115610512576020820191506020810190506020830392506104ee565b505050905090810190601f16801561053e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b6002543410156105605760006000fd5b806000908051906020019061057692919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528381815181526020019150805190602001908083836000831461065c575b80518252602083111561065c57602082019150602081019050602083039250610638565b505050905090810190601f1680156106885780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b60025481565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107365780601f1061070b57610100808354040283529160200191610736565b820191906000526020600020905b81548152906001019060200180831161071957829003601f168201915b505050505081565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561079b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc670de0b6b3a76400009081150290604051809050600060405180830381858888f1935050505015156107e357fe5b5b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108445760006000fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108e65760006000fd5b806002819055507fa6dc15bdb68da224c66db4b3838d9a2b205138e8cff6774e57d0af91e196d6226002546040518082815260200191505060405180910390a15b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061096b57805160ff1916838001178555610999565b82800160010185558215610999579182015b8281111561099857825182559160200191906001019061097d565b5b5090506109a691906109aa565b5090565b6109cc91905b808211156109c85760008160009055506001016109b0565b5090565b905600a165627a7a72305820898aa45f7eb811ccffb977046caf5344261459d9dfea6db4d4441ccfa68b86ea0029")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x1"))
o := common.HexToAddress("0xaaa2")
bal := uint256.MustFromHex("0xfffffffffffffffffffffff")
sdb.SetBalance(o, bal, tracing.BalanceChangeUnspecified)
sdb.Finalise(false)
sdb2 := sdb.Copy()
// We run the first transaction.
getHash := func(n uint64) common.Hash {
return common.Hash{}
}
txCtx := vm.TxContext{
Origin: o,
GasPrice: big.NewInt(0),
}
blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: 0,
BaseFee: big.NewInt(0),
}
newUint64 := func(val uint64) *uint64 { return &val }
chainConfig := ¶ms.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
ShanghaiTime: newUint64(0),
Ethash: new(params.EthashConfig),
}
evm := vm.NewEVM(blockCtx, txCtx, sdb2, chainConfig, vm.Config{})
data, _ := hex.DecodeString("955e136f")
toCopy := common.BytesToAddress(to.Bytes())
msg := &core.Message{
To: &toCopy,
From: o,
Nonce: 0,
Value: big.NewInt(0xffffffffffffff),
GasLimit: 0x7d000,
GasPrice: big.NewInt(0),
GasFeeCap: big.NewInt(0),
GasTipCap: big.NewInt(0),
Data: data,
AccessList: nil,
SkipAccountChecks: true,
}
_, appErr := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr1: %v\n", appErr)
// "Fix 1": uncommenting the following line.
// sdb2.Finalise(false)
sdb3 := sdb2.Copy()
o2 := common.HexToAddress("0xaaa3")
bal2 := uint256.MustFromHex("0xfffffffffffffffffffffff")
sdb3.SetBalance(o2, bal2, tracing.BalanceChangeUnspecified)
// "Fix 2": using "Commit" instead of "Finalize" in the following line.
sdb3.Finalise(false)
sdb4 := sdb3.Copy()
// We run the second transaction which crashes.
txCtx2 := vm.TxContext{
Origin: o2,
GasPrice: big.NewInt(0),
}
blockCtx2 := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHash,
GasLimit: 0x7d000,
BlockNumber: big.NewInt(1),
Coinbase: common.HexToAddress("0xaaa0"),
Difficulty: big.NewInt(0),
Time: 0,
BaseFee: big.NewInt(0),
}
evm2 := vm.NewEVM(blockCtx2, txCtx2, sdb4, chainConfig, vm.Config{})
data2, _ := hex.DecodeString("8db0cb21000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000129")
toCopy2 := common.BytesToAddress(to.Bytes())
msg2 := &core.Message{
To: &toCopy2,
From: o2,
Nonce: 0,
Value: big.NewInt(0),
GasLimit: 0x7d000,
GasPrice: big.NewInt(0),
GasFeeCap: big.NewInt(0),
GasTipCap: big.NewInt(0),
Data: data2,
AccessList: nil,
SkipAccountChecks: true,
}
_, appErr2 := core.ApplyMessage(evm2, msg2, new(core.GasPool).AddGas(0x7d0000))
fmt.Printf("AppErr2: %v\n", appErr2)
}
|
Well, not from my end. On the other hand, I don't know the exact particulars of this crash. But the PR I linked is the one that fixes the core underlying problem (original vs copy vs copy-of-copy) |
@holiman Got it! Thanks! I assume it's difficult to tell in retrospect. I mostly wanted to bring it to your attention in case it rings any bells. 😊 |
I observed a crash in
gasSStoreEIP2200
due to a panic inSubRefund
. Unfortunately, I don't have a convenient repro for this since it happend during a run of our Harvey fuzzer on the following contract: https://github.com/SmartContractSecurity/SWC-registry/blob/8a3878fec7785d1d06e0857784170340fdfb438b/test_cases/solidity/assert_violations/gas_model/gas_model.sol.However, looking at the code of
gasSStoreEIP2200
andSubRefund
I wonder why this error isn't returned bySubRefund
and handled ingasSStoreEIP2200
(maybe by terminating the execution with an error).System information
OS & Version: Windows
Commit hash : a308f01
Expected behaviour
The execution of a transaction shouldn't crash.
Actual behaviour
The execution of a transaction crashes
The text was updated successfully, but these errors were encountered: