From 1655f801d1df53f3914c9a6db77936c18acf6b55 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Thu, 5 Feb 2026 15:27:40 +0800 Subject: [PATCH] core/vm: add read only protection for opcodes #33637 (#1984) --- core/vm/instructions.go | 12 ++++++++++++ core/vm/operations_acl.go | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 6c365ef7b2..da8b8bccf1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -526,6 +526,9 @@ func opSload(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSstore(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { + if evm.readOnly { + return nil, ErrWriteProtection + } loc := scope.Stack.pop() val := scope.Stack.pop() evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) @@ -757,6 +760,9 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { // Get the arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) + if evm.readOnly && !value.IsZero() { + return nil, ErrWriteProtection + } var bigVal = big0 // TODO(daniel): use uint256.Int instead of converting with toBig() // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls), @@ -901,6 +907,9 @@ func opStop(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { + if evm.readOnly { + return nil, ErrWriteProtection + } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) @@ -917,6 +926,9 @@ func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { } func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { + if evm.readOnly { + return nil, ErrWriteProtection + } beneficiary := scope.Stack.pop() balance := evm.StateDB.GetBalance(scope.Contract.Address()) evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 452edd96d1..0471f21d6c 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -244,8 +244,10 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { evm.StateDB.AddAddressToAccessList(address) gas = params.ColdAccountAccessCostEIP2929 + // Terminate the gas measurement if the leftover gas is not sufficient, + // it can effectively prevent accessing the states in the following steps if contract.Gas < gas { - return gas, nil + return 0, ErrOutOfGas } } // if empty and transfers value