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

Possible VM bug in EIP 3860 #1923

Closed
jochem-brouwer opened this issue May 30, 2022 · 11 comments
Closed

Possible VM bug in EIP 3860 #1923

jochem-brouwer opened this issue May 30, 2022 · 11 comments

Comments

@jochem-brouwer
Copy link
Member

Upon refactoring I stumbled across this:

if (this._common.isActivatedEIP(3860)) {

The contract nonce is updated, and then the maxInitCodeSize check is done. Before that there are checks on the nonce, call depth, and balance checks. It seems to me that if those checks are violated then no further gas is consumed (so not the provided gasLimit is consumed) and the frame is directly returned. However upon the maxInitCodeSize check it seems that the nonce is updated, so this state change dangles (and seems wrong to me). I think there are tests for this EIP somewhere in an ethereum/tests PR, I do recall testing it, but this situation was not caught (it should catch this as a state root error though, since the nonce is 1 too high if the maxInitCodeSize is violated..?)

@ryanio
Copy link
Contributor

ryanio commented May 30, 2022

is the state reverted if it throws there? in that case the nonce wouldn't be updated in the trie.

it does make sense to me that the check can be moved up a few lines before the nonce update. (edit: andrei clarified below in case of failure nonce should still be updated)

looking further though, does it catch here first? is there a reason why we would have this check in two places

if (this._vm._common.isActivatedEIP(3860)) {
if (message.data.length > this._vm._common.param('vm', 'maxInitCodeSize')) {
return {
gasUsed: message.gasLimit,
createdAddress: message.to,
execResult: {
returnValue: Buffer.alloc(0),
exceptionError: new VmError(ERROR.INITCODE_SIZE_VIOLATION),
gasUsed: message.gasLimit,
},
}
}
}

@axic
Copy link
Member

axic commented May 30, 2022

@gumb0 @jochem-brouwer is there anything we need to clarify in terms of error cases in the EIP?

@jochem-brouwer
Copy link
Member Author

I think I just gotta see and test it, but if you CREATE/CREATE2 it first calls into EEI's create opcode which later (below the check of maxInitCodeSize) calls back into EVMs executeMessage, which then calls into _executeCreate. In that case it throws, it seems like different behavior in CREATE/CREATE2 than in txn and I do not think this is intended by the EIP.

@axic I think the EIP is clear enough here, but just to check, in case of the CREATE/CREATE2 opcode, if we try to deploy a contract with initcode > maxInitCodeSize, then 0 is put on the stack and the upfront cost of the CREATE (so the base fee, memory expansion, possible hashing cost) is spent in gas, and not the entire gasLimit? Our implementation (probably) increases the nonce of the contract which invokes CREATE/CREATE2 by 1 even if the maxInitCodeSize is violated, which I assume should not happen?

@jochem-brouwer
Copy link
Member Author

@axic nvm, just saw my comment on the ethereum/tests PR, I don't think the EIP is up-to-date yet? ethereum/tests#990 (comment) CC @gumb0

@gumb0
Copy link

gumb0 commented May 31, 2022

@axic nvm, just saw my comment on the ethereum/tests PR, I don't think the EIP is up-to-date yet? ethereum/tests#990 (comment) CC @gumb0

Yes, we have not updated it yet, sorry.

@axic I think the EIP is clear enough here, but just to check, in case of the CREATE/CREATE2 opcode, if we try to deploy a contract with initcode > maxInitCodeSize, then 0 is put on the stack and the upfront cost of the CREATE (so the base fee, memory expansion, possible hashing cost) is spent in gas, and not the entire gasLimit? Our implementation (probably) increases the nonce of the contract which invokes CREATE/CREATE2 by 1 even if the maxInitCodeSize is violated, which I assume should not happen?

When initcode exceeds limit, gas for initcode execution is not deducted (so only upfront charges are: base cost, memory expansion, hashing, initcode charge, too).

Nonce is updated even in case of failure, which is similar to other creation errors like contract already existing or error during initcode execution.

@holgerd77
Copy link
Member

Not 100% sure, can this be closed (then please do)?

@jochem-brouwer
Copy link
Member Author

The tests are not yet updated so we can't check against the existing implementation, so I'd say that this issue is not yet closed until the tests are finalized (the ethereum/tests tests of this EIP)

@gumb0
Copy link

gumb0 commented Jul 7, 2022

The tests are not yet updated so we can't check against the existing implementation, so I'd say that this issue is not yet closed until the tests are finalized (the ethereum/tests tests of this EIP)

I think tests had correct behavior all along (no update expected), and EIP wording was updated recently ethereum/EIPs#5130

@holgerd77
Copy link
Member

Can't completely judge this issue, can this be closed?

//cc @jochem-brouwer

@holgerd77
Copy link
Member

(if so please do)

@jochem-brouwer
Copy link
Member Author

Yep this is indeed resolved, should have closed before.

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

No branches or pull requests

5 participants