Skip to content
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

Closed
wuestholz opened this issue Sep 24, 2019 · 27 comments
Closed

Crash in gasSStoreEIP2200 due to panic in SubRefund #20119

wuestholz opened this issue Sep 24, 2019 · 27 comments

Comments

@wuestholz
Copy link
Contributor

I observed a crash in gasSStoreEIP2200 due to a panic in SubRefund. 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 and SubRefund I wonder why this error isn't returned by SubRefund and handled in gasSStoreEIP2200 (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

@wuestholz
Copy link
Contributor Author

I observed the same crash when fuzzing this slight simpler contract:

pragma solidity ^0.5.11;

contract Foo {
  int x = 1;
  function IncrX() public {
    x++;
  }
}

This is the corresponding (optimized) bytecode:

60806040526001600055348015601457600080fd5b506075806100236000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806343c9250414602d575b600080fd5b60336035565b005b60008054600101905556fea265627a7a723158204ab640601aa22be0d859e025aa75f73ee1738552892def5cfef1eaaaa49a8f3f64736f6c634300050b0032

@karalabe
Copy link
Member

I've tried to repro this on a local dev chain, but couldn't:

$ geth --dev console
> eth.sendTransaction({from: eth.accounts[0], data: "0x60806040526001600055348015601457600080fd5b506075806100236000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806343c9250414602d575b600080fd5b60336035565b005b60008054600101905556fea265627a7a723158204ab640601aa22be0d859e025aa75f73ee1738552892def5cfef1eaaaa49a8f3f64736f6c634300050b0032"})

INFO [09-24|14:35:40.094] Submitted contract creation              fullhash=0x448b70c65a88f4e8c521d93f21a700828d3950189e717fe81422c0c589aa317a contract=0x72Dcd3a8f8Ac8b6979D29460FE74C3E60b4bC4Ef
"0x448b70c65a88f4e8c521d93f21a700828d3950189e717fe81422c0c589aa317a"
> eth.getTransactionReceipt("0x448b70c65a88f4e8c521d93f21a700828d3950189e717fe81422c0c589aa317a")
{
  blockHash: "0xef393ee285224b9d5734763a7bf652eba3b157cd740b3194ae592c04c103db9e",
  blockNumber: 1,
  contractAddress: "0x72dcd3a8f8ac8b6979d29460fe74c3e60b4bc4ef",
  cumulativeGasUsed: 98769,
  from: "0xb69023240c0a2a90b622a2f2861f3203e060995b",
  gasUsed: 98769,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: null,
  transactionHash: "0x448b70c65a88f4e8c521d93f21a700828d3950189e717fe81422c0c589aa317a",
  transactionIndex: 0
}
> eth.sendTransaction({from: eth.accounts[0], to: "0xB69023240c0a2A90B622a2F2861f3203E060995b", data: "0x43c92504"})

INFO [09-24|14:38:07.606] Submitted transaction                    fullhash=0xa718c60296c67bc36e5578e4fc6c2053cd2f8d1f6481cefde113dc77f1877d79 recipient=0xB69023240c0a2A90B622a2F2861f3203E060995b
"0xa718c60296c67bc36e5578e4fc6c2053cd2f8d1f6481cefde113dc77f1877d79"
> eth.getTransactionReceipt("0xa718c60296c67bc36e5578e4fc6c2053cd2f8d1f6481cefde113dc77f1877d79")
{
  blockHash: "0x0b83654bef61de393c41100f5ec602ae4dddde7fee085bfb45dab39e30c82266",
  blockNumber: 2,
  contractAddress: null,
  cumulativeGasUsed: 21064,
  from: "0xb69023240c0a2a90b622a2f2861f3203e060995b",
  gasUsed: 21064,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: "0xb69023240c0a2a90b622a2f2861f3203e060995b",
  transactionHash: "0xa718c60296c67bc36e5578e4fc6c2053cd2f8d1f6481cefde113dc77f1877d79",
  transactionIndex: 0
}00000000000",
  status: "0x1",
  to: "0xb69023240c0a2a90b622a2f2861f3203e060995b",
  transactionHash: "0xa718c60296c67bc36e5578e4fc6c2053cd2f8d1f6481cefde113dc77f1877d79",
  transactionIndex: 0
}

@karalabe
Copy link
Member

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:

> eth.sendTransaction({from: eth.accounts[0], data: "0x60806040526001600055348015601457600080fd5b506075806100236000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806343c9250414602d575b600080fd5b60336035565b005b60008054600101905556fea265627a7a7231582034712a5ef24e29b8291d4e74cd8b28d183c19bdfbc28566401b76a7c13a7f53964736f6c634300050b0032"})
INFO [09-24|14:43:52.803] Submitted contract creation              fullhash=0x599707bd62188c082bc439ca85d7bdf14d592808400b5dd263b44686bf1242d0 contract=0xC02e6Ce1874B4F02a9cd08760046Ab00967a770a
> eth.sendTransaction({from: eth.accounts[0], to: "0xC02e6Ce1874B4F02a9cd08760046Ab00967a770a", data: "0x43c92504"})
> eth.getTransactionReceipt("0x344c3f69f2856530dac184af85990de3330a27f6b43a8e1bfe4d35df0a39e357")
{
  blockHash: "0x7599a550342758b67a4b57a1b38bc7f00c69f186dab76865696a85ab634b6979",
  blockNumber: 4,
  contractAddress: null,
  cumulativeGasUsed: 27001,
  from: "0xb69023240c0a2a90b622a2f2861f3203e060995b",
  gasUsed: 27001,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: "0xc02e6ce1874b4f02a9cd08760046ab00967a770a",
  transactionHash: "0x344c3f69f2856530dac184af85990de3330a27f6b43a8e1bfe4d35df0a39e357",
  transactionIndex: 0
}

Still no crash though.

@karalabe
Copy link
Member

Perhaps you are running a more complex suite with contract interactions? Could you share a repro that we could run standalone?

@wuestholz
Copy link
Contributor Author

@karalabe Thanks a lot for looking into this so quickly. It took me a while to reproduce it, but here's the resulting code:

getHash := func(n uint64) common.Hash {
	return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
}

to := common.HexToAddress("0x0901d12ebe1b195e5aa8748e62bd7734ae19b51f")
gp, _ := math.ParseBig256("0x773594000")
bn, _ := math.ParseBig256("0x661a55")
di, _ := math.ParseBig256("0xc8372c8c4a6f6")
bt, _ := math.ParseBig256("0x5be99aa8")
o := common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0")
gasPool := new(core.GasPool).AddGas(0x7d0000)

ctx := vm.Context{
	CanTransfer: core.CanTransfer,
	Transfer:    core.Transfer,
	GetHash:     getHash,
	GasPrice:    gp,
	Origin:      o,
	GasLimit:    0x27100,
	BlockNumber: bn,
	Coinbase:    common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0"),
	Difficulty:  di,
	Time:        bt,
}

chainConfig := &params.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),
}

evmConfig := vm.Config{}

db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db))
bal, _ := math.ParseBig256("0x000000000000000000000000000000000000010000000000000b3a8e651d3fff")
sdb.SetBalance(o, bal)
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("6080604052348015600f57600080fd5b506004361060285760003560e01c806343c9250414602d575b600080fd5b60336035565b005b60008054600101905556fea265627a7a723158204ab640601aa22be0d859e025aa75f73ee1738552892def5cfef1eaaaa49a8f3f64736f6c634300050b0032")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x1"))
sdb.Commit(false)

// Without the following line it doesn't crash:
sdb.SetState(to, common.HexToHash("0x0"), common.HexToHash("0x0"))

evm := vm.NewEVM(ctx, sdb.Copy() , chainConfig, evmConfig)

in := "43c9250400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
data, _ := hex.DecodeString(in)
tc := common.BytesToAddress(to.Bytes())
msg := types.NewMessage(o, &tc, 0, big.NewInt(0), 0x27100, gp, data, false)
core.ApplyMessage(evm, msg, gasPool)

It seems like the SetState instruction after the Commit is crucial for some reason. Not quite sure why.

@karalabe
Copy link
Member

karalabe commented Sep 24, 2019

Ah, I think I see what the issue is.

When you call sdb.Commit(false), you essentially simulate a new block being started. This is valid and does exactly what block processing does.

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 sdb.SetState, never spending any gas on it. So when you reset the slot to its original value afterwards in a real transaction, you get back more gas than you spent.

Here's your test case, cleaned up a bit (I put it into core/blockchain_test.go):

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 sdb.Finalize flattening any dirtyness (if you remove that, it will panic). This simulates an end of a previous transaction. You must finalize any modifications before you can execute an EVM call on top, otherwise you are executing code that cannot happen based on the execution model of the EVM.


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.

@wuestholz
Copy link
Contributor Author

@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 ApplyMessage you again end up in a "fully finalized" state with no dirty writes.

@karalabe
Copy link
Member

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.

@wuestholz
Copy link
Contributor Author

wuestholz commented Oct 2, 2019

@karalabe I came across another such crash for the following piece of code:

getHash := func(n uint64) common.Hash {
	return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
}

to := common.HexToAddress("0x0901d12ebe1b195e5aa8748e62bd7734ae19b51f")
gp, _ := math.ParseBig256("0x2fc60325c")
gp2, _ := math.ParseBig256("0xbb73954fa")
bn, _ := math.ParseBig256("0x661a7f")
di, _ := math.ParseBig256("0xa7d7343662e26")
bt, _ := math.ParseBig256("0x5bf7a462")
o := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
o2 := common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2")
gasPool := new(core.GasPool).AddGas(0x7d0000)

ctx := vm.Context{
	CanTransfer: core.CanTransfer,
	Transfer:    core.Transfer,
	GetHash:     getHash,
	GasPrice:    gp,
	Origin:      o,
	GasLimit:    0x7d000,
	BlockNumber: bn,
	Coinbase:    common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0"),
	Difficulty:  di,
	Time:        bt,
}

chainConfig := &params.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),
}

evmConfig := vm.Config{}

db := rawdb.NewMemoryDatabase()
sdb, _ := state.New(common.Hash{}, state.NewDatabase(db))
bal, _ := math.ParseBig256("0x0000000000000000000000000000000000ffffffffffffffffac8146179a7fff")
sdb.SetBalance(o, bal)
sdb.SetNonce(to, 1)
code, _ := hex.DecodeString("60606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630e3391ec1461009957806313678f70146100eb5780638db0cb2114610121578063955e136f1461017b578063a035b1fe146101cd578063a7695848146101f3578063cfe25be01461028c578063f2853292146102c2578063fb92488b146102f8575bfe5b34156100a157fe5b6100a9610318565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100f357fe5b61011f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061033e565b005b341561012957fe5b610179600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b7565b005b6101cb600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610550565b005b34156101d557fe5b6101dd61069a565b6040518082815260200191505060405180910390f35b34156101fb57fe5b6102036106a0565b6040518080602001828103825283818151815260200191508051906020019080838360008314610252575b8051825260208311156102525760208201915060208101905060208303925061022e565b505050905090810190601f16801561027e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561029457fe5b6102c0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073e565b005b34156102ca57fe5b6102f6600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107e7565b005b341561030057fe5b6103166004808035906020019091905050610889565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561039b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff16ff5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16313373ffffffffffffffffffffffffffffffffffffffff16311115156104165760006000fd5b806000908051906020019061042c92919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360008314610512575b805182526020831115610512576020820191506020810190506020830392506104ee565b505050905090810190601f16801561053e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b6002543410156105605760006000fd5b806000908051906020019061057692919061092a565b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f61ad394942e3b5e898c6ee4bbd13ed40a2a64d0c82990b3ce067ffc3997b9c503382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528381815181526020019150805190602001908083836000831461065c575b80518252602083111561065c57602082019150602081019050602083039250610638565b505050905090810190601f1680156106885780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b50565b60025481565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107365780601f1061070b57610100808354040283529160200191610736565b820191906000526020600020905b81548152906001019060200180831161071957829003601f168201915b505050505081565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561079b5760006000fd5b8073ffffffffffffffffffffffffffffffffffffffff166108fc670de0b6b3a76400009081150290604051809050600060405180830381858888f1935050505015156107e357fe5b5b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108445760006000fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108e65760006000fd5b806002819055507fa6dc15bdb68da224c66db4b3838d9a2b205138e8cff6774e57d0af91e196d6226002546040518082815260200191505060405180910390a15b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061096b57805160ff1916838001178555610999565b82800160010185558215610999579182015b8281111561099857825182559160200191906001019061097d565b5b5090506109a691906109aa565b5090565b6109cc91905b808211156109c85760008160009055506001016109b0565b5090565b905600a165627a7a72305820898aa45f7eb811ccffb977046caf5344261459d9dfea6db4d4441ccfa68b86ea0029")
sdb.SetCode(to, code)
sdb.SetState(to, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000047"))
sdb.SetState(to, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"), common.HexToHash("0x0000000000000000000000000901d12ebe1b195e5aa8748e62bd7734ae19b51f"))
sdb.SetState(to, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"), common.HexToHash("0x00000000000000000000000000000000000000000000000000b1a2bc2ec50000"))
sdb.SetState(to, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000003"), common.HexToHash("0x000000000000000000000000dcac757ec162e8b4d9766ab75b4e4f57b0f32205"))
sdb.SetState(to, common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"), common.HexToHash("0x426520746865206669727374206f6e6520746f2073686f7720796f7572207374"))
sdb.SetState(to, common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564"), common.HexToHash("0x61636b0000000000000000000000000000000000000000000000000000000000"))
sdb.Finalise(true)

sdb2 := sdb.Copy()
evm := vm.NewEVM(ctx, sdb2, chainConfig, evmConfig)

in := "955e136f00000000000001b94478286b21bbb4ae0150506392aaaaaaaaaaaaaaaaaaaaab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000c200000000000000000000"
data, _ := hex.DecodeString(in)
tc := common.BytesToAddress(to.Bytes())
val, _ := math.ParseBig256("0xffff00000000000006f4527ae609e5")
msg := types.NewMessage(o, &tc, 0, val, 0x7d000, gp, data, false)
_, _, _, appErr := core.ApplyMessage(evm, msg, gasPool)
fmt.Printf("AppErr1: %v\n", appErr)

ctx2 := vm.Context{
	CanTransfer: core.CanTransfer,
	Transfer:    core.Transfer,
	GetHash:     getHash,
	GasPrice:    gp2,
	Origin:      o2,
	GasLimit:    0x7d000,
	BlockNumber: bn,
	Coinbase:    common.HexToAddress("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0"),
	Difficulty:  di,
	Time:        bt,
}

sdb3 := sdb2.Copy()
bal2, _ := math.ParseBig256("0x000000000000000000000000000000000000012e469127d14f63ecff0de82a31")
sdb3.SetBalance(o2, bal2)
sdb3.Finalise(false)

evm2 := vm.NewEVM(ctx2, sdb3.Copy(), chainConfig, evmConfig)

in2 := "8db0cb21000000000000000000000000000000000000000000000000000000000000002d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000001290000000000000000000000000000000000000000000000"
data2, _ := hex.DecodeString(in2)
tc2 := common.BytesToAddress(to.Bytes())
val2, _ := math.ParseBig256("0x0")
msg2 := types.NewMessage(o2, &tc2, 0, val2, 0x7d000, gp2, data2, false)
_, _, _, appErr2 := core.ApplyMessage(evm2, msg2, gasPool)
fmt.Printf("AppErr2: %v\n", appErr2)

Any idea why that might happen? It seems like all state DBs are finalized before applying the transactions.

@holiman
Copy link
Contributor

holiman commented Oct 2, 2019 via email

@karalabe
Copy link
Member

karalabe commented Oct 2, 2019

Though, if sdb3 is then finalized, should it matter? Could it be that you reuse the same gasPool instance in 2 transaction invocations?

@wuestholz
Copy link
Contributor Author

@holiman @karalabe Thanks a lot for looking into this so quickly!

Yeah, I was thinking the same as @karalabe. I would assume that the Finalize for sdb3 would make sure the above invariant is preserved.

About the gas pool: it crashes even when using a second gas pool.

@wuestholz
Copy link
Contributor Author

wuestholz commented Oct 3, 2019

@holiman I just tried adding sdb2.Finalise(false) before sdb3 := sdb2.Copy(). This prevents the crash, but I don't understand why... Doing sdb3.Finalise(false) after the copy still crashes.

@wuestholz
Copy link
Contributor Author

@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 := &params.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 Commit instead of Finalize. However, I don't understand why either of the two "fixes" prevent the crash. Do you have an explanation?

@wuestholz
Copy link
Contributor Author

@karalabe @holiman Are you planning to look into the somewhat strange behavior above? It would be great if I could understand why this happens to make sure I use the API correctly in our Harvey fuzzer. Thanks again!

@wuestholz
Copy link
Contributor Author

wuestholz commented Apr 6, 2022

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 := &params.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)

@wuestholz
Copy link
Contributor Author

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 := &params.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)

@karalabe
Copy link
Member

karalabe commented Dec 7, 2023

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).

@wuestholz
Copy link
Contributor Author

wuestholz commented Dec 7, 2023

@karalabe Thanks a lot for taking another look! 🙏

Out of the three options, I like the second one the least (implicity calling Finalize). :) It feels like the third option (retain more dirty tracking, possibly under some option) may be the most flexible option (from a user's perspective), but I can also understand if there are good reasons to prefer the first option.

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?

@wuestholz
Copy link
Contributor Author

@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
Copy link
Contributor

holiman commented Apr 9, 2024

We do plan for a different fix. First, it required #28912 to be merged, making it possible to simplify journalling around create/destruct: we no longer need to reference a live object in the journal.

Secondly, we need #28666, which actually changes the journalling.

@wuestholz
Copy link
Contributor Author

@holiman Thanks a lot for the update! Will these changes implicitly implement the the third option from above (retaining more dirty tracking in Copy? Or will it change when Copy can be called (for instance, requiring the state to be finalized)?

@holiman
Copy link
Contributor

holiman commented Apr 24, 2024

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

@holiman
Copy link
Contributor

holiman commented Apr 24, 2024

Example updated to latest code, main.go

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 := &params.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)
}
$ go run ./cmd/wuestholz/
AppErr1: <nil>
AppErr2: <nil>

@holiman holiman closed this as completed Apr 24, 2024
@wuestholz
Copy link
Contributor Author

@holiman @karalabe Thanks a lot for the fix! I can confirm that there is no crash for v1.14.0. However, I noticed that the crash already "disappears" when switching from v1.13.10 to v1.13.11. Is this expected? Was there a separate bug that was already fixed earlier? 🤔

@holiman
Copy link
Contributor

holiman commented Apr 25, 2024

Is this expected?

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)

@wuestholz
Copy link
Contributor Author

@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. 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants