diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs index 2825bb7e5a8..1c3c0599562 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmStackBenchmarks.cs @@ -47,27 +47,6 @@ public UInt256 Uint256(UInt256 v) return value; } - [Benchmark(OperationsPerInvoke = 4)] - [ArgumentsSource(nameof(ValueSource))] - public Int256.Int256 Int256(UInt256 v) - { - EvmStack stack = new(_stack.AsSpan(), 0, NullTxTracer.Instance); - - stack.PushSignedInt256(new Int256.Int256(v)); - stack.PopSignedInt256(out Int256.Int256 value); - - stack.PushSignedInt256(value); - stack.PopSignedInt256(out value); - - stack.PushSignedInt256(value); - stack.PopSignedInt256(out value); - - stack.PushSignedInt256(value); - stack.PopSignedInt256(out value); - - return value; - } - [Benchmark(OperationsPerInvoke = 4)] public byte Byte() { diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 48dc9ae49a2..b59af410489 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -218,13 +218,6 @@ public void PopLimbo() } } - public void PopSignedInt256(out Int256.Int256 result) - { - // tail call into UInt256 - Unsafe.SkipInit(out result); - PopUInt256(out Unsafe.As(ref result)); - } - /// /// Pops an Uint256 written in big endian. /// @@ -233,9 +226,11 @@ public void PopSignedInt256(out Int256.Int256 result) /// All it does is and then reverse endianness if needed. Then it creates . /// /// The returned value. - public void PopUInt256(out UInt256 result) + public bool PopUInt256(out UInt256 result) { + Unsafe.SkipInit(out result); ref byte bytes = ref PopBytesByRef(); + if (Unsafe.IsNullRef(ref bytes)) return false; if (Avx2.IsSupported) { @@ -271,6 +266,8 @@ public void PopUInt256(out UInt256 result) result = new UInt256(u0, u1, u2, u3); } + + return true; } public readonly bool PeekUInt256IsZero() @@ -300,7 +297,7 @@ public Address PopAddress() { if (Head-- == 0) { - EvmStack.ThrowEvmStackUnderflowException(); + return null; } return new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); @@ -310,7 +307,7 @@ public ref byte PopBytesByRef() { if (Head-- == 0) { - EvmStack.ThrowEvmStackUnderflowException(); + return ref Unsafe.NullRef(); } return ref _bytes[Head * WordSize]; @@ -356,9 +353,9 @@ public void PushLeftPaddedBytes(Span value, int paddingLength) } } - public void Dup(in int depth) + public bool Dup(in int depth) { - EnsureDepth(depth); + if (!EnsureDepth(depth)) return false; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); @@ -376,19 +373,23 @@ public void Dup(in int depth) { EvmStack.ThrowEvmStackOverflowException(); } + + return true; } - public readonly void EnsureDepth(int depth) + public readonly bool EnsureDepth(int depth) { if (Head < depth) { - EvmStack.ThrowEvmStackUnderflowException(); + return false; } + + return true; } - public readonly void Swap(int depth) + public readonly bool Swap(int depth) { - EnsureDepth(depth); + if (!EnsureDepth(depth)) return false; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); @@ -403,6 +404,8 @@ public readonly void Swap(int depth) { Trace(depth); } + + return true; } private readonly void Trace(int depth) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index c168656d7ba..3ddd12fbc2d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -811,8 +811,8 @@ private CallResult ExecuteCode= BigInt32) { - stack.EnsureDepth(1); + if (!stack.EnsureDepth(1)) goto StackUnderflow; break; } @@ -1018,8 +1018,8 @@ private CallResult ExecuteCode b) { stack.PushOne(); @@ -1052,8 +1052,8 @@ private CallResult ExecuteCode(ref a).CompareTo(As(ref b)) < 0) { @@ -1070,8 +1070,8 @@ private CallResult ExecuteCode(ref a).CompareTo(As(ref b)) > 0) { stack.PushOne(); @@ -1087,8 +1087,8 @@ private CallResult ExecuteCode aVec = ReadUnaligned>(ref stack.PopBytesByRef()); - Vector256 bVec = ReadUnaligned>(ref stack.PopBytesByRef()); + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 aVec = ReadUnaligned>(ref bytesRef); + + bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 bVec = ReadUnaligned>(ref bytesRef); WriteUnaligned(ref stack.PushBytesRef(), Vector256.BitwiseAnd(aVec, bVec)); break; @@ -1130,8 +1135,13 @@ private CallResult ExecuteCode aVec = ReadUnaligned>(ref stack.PopBytesByRef()); - Vector256 bVec = ReadUnaligned>(ref stack.PopBytesByRef()); + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 aVec = ReadUnaligned>(ref bytesRef); + + bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 bVec = ReadUnaligned>(ref bytesRef); WriteUnaligned(ref stack.PushBytesRef(), Vector256.BitwiseOr(aVec, bVec)); break; @@ -1140,8 +1150,13 @@ private CallResult ExecuteCode aVec = ReadUnaligned>(ref stack.PopBytesByRef()); - Vector256 bVec = ReadUnaligned>(ref stack.PopBytesByRef()); + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 aVec = ReadUnaligned>(ref bytesRef); + + bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + Vector256 bVec = ReadUnaligned>(ref bytesRef); WriteUnaligned(ref stack.PushBytesRef(), Vector256.Xor(aVec, bVec)); break; @@ -1150,7 +1165,10 @@ private CallResult ExecuteCode negVec = Vector256.OnesComplement(ReadUnaligned>(ref stack.PopBytesByRef())); + ref byte bytesRef = ref stack.PopBytesByRef(); + if (IsNullRef(ref bytesRef)) goto StackUnderflow; + + Vector256 negVec = Vector256.OnesComplement(ReadUnaligned>(ref bytesRef)); WriteUnaligned(ref stack.PushBytesRef(), negVec); break; @@ -1159,7 +1177,7 @@ private CallResult ExecuteCode= BigInt32) @@ -1182,8 +1200,8 @@ private CallResult ExecuteCode _returnDataBuffer.Length) @@ -1432,7 +1455,7 @@ private CallResult ExecuteCode long.MaxValue ? long.MaxValue : (long)a; Hash256 blockHash = _blockhashProvider.GetBlockhash(blkCtx.Header, number); stack.PushBytes(blockHash != null ? blockHash.Bytes : BytesZero32); @@ -1528,7 +1551,7 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec)) - goto OutOfGas; + exceptionType = InstructionSStore(vmState, ref stack, ref gasAvailable, spec); + if (exceptionType != EvmExceptionType.None) goto ReturnFailure; break; } @@ -1633,7 +1656,7 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec, instruction, out returnData); - if (exceptionType != EvmExceptionType.None) - { - goto ReturnFailure; - } + if (exceptionType != EvmExceptionType.None) goto ReturnFailure; + if (returnData is null) { break; @@ -1844,7 +1867,8 @@ private CallResult ExecuteCode= 256UL) { stack.PopLimbo(); @@ -1882,7 +1907,7 @@ private CallResult ExecuteCode= 256) { stack.PopLimbo(); @@ -1903,7 +1928,7 @@ private CallResult ExecuteCode> (int)a.u0; stack.PushUInt256(in result); } @@ -1916,8 +1941,8 @@ private CallResult ExecuteCode= BigInt256) { if (As(ref b).Sign >= 0) @@ -1944,6 +1969,7 @@ private CallResult ExecuteCode( if (instruction == Instruction.DELEGATECALL && !spec.DelegateCallEnabled || instruction == Instruction.STATICCALL && !spec.StaticCallEnabled) return EvmExceptionType.BadInstruction; - stack.PopUInt256(out UInt256 gasLimit); + if (!stack.PopUInt256(out UInt256 gasLimit)) return EvmExceptionType.StackUnderflow; Address codeSource = stack.PopAddress(); + if (codeSource is null) return EvmExceptionType.StackUnderflow; if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, spec)) return EvmExceptionType.OutOfGas; @@ -2175,15 +2205,15 @@ private EvmExceptionType InstructionCall( callValue = env.Value; break; default: - stack.PopUInt256(out callValue); + if (!stack.PopUInt256(out callValue)) return EvmExceptionType.StackUnderflow; break; } UInt256 transferValue = instruction == Instruction.DELEGATECALL ? UInt256.Zero : callValue; - stack.PopUInt256(out UInt256 dataOffset); - stack.PopUInt256(out UInt256 dataLength); - stack.PopUInt256(out UInt256 outputOffset); - stack.PopUInt256(out UInt256 outputLength); + if (!stack.PopUInt256(out UInt256 dataOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 dataLength)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 outputOffset)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 outputLength)) return EvmExceptionType.StackUnderflow; if (vmState.IsStatic && !transferValue.IsZero && instruction != Instruction.CALLCODE) return EvmExceptionType.StaticCallViolation; @@ -2303,48 +2333,53 @@ private EvmExceptionType InstructionCall( } [SkipLocalsInit] - private static bool InstructionRevert(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) + private static EvmExceptionType InstructionRevert(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) where TTracing : struct, IIsTracing { - stack.PopUInt256(out UInt256 position); - stack.PopUInt256(out UInt256 length); + SkipInit(out returnData); + + if (!stack.PopUInt256(out UInt256 position) || + !stack.PopUInt256(out UInt256 length)) + return EvmExceptionType.StackUnderflow; if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) { - returnData = null; - return false; + return EvmExceptionType.OutOfGas; } returnData = vmState.Memory.Load(in position, in length).ToArray(); - return true; + return EvmExceptionType.None; } [SkipLocalsInit] - private static bool InstructionReturn(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) + private static EvmExceptionType InstructionReturn(EvmState vmState, ref EvmStack stack, ref long gasAvailable, out object returnData) where TTracing : struct, IIsTracing { - stack.PopUInt256(out UInt256 position); - stack.PopUInt256(out UInt256 length); + SkipInit(out returnData); + + if (!stack.PopUInt256(out UInt256 position) || + !stack.PopUInt256(out UInt256 length)) + return EvmExceptionType.StackUnderflow; if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) { - returnData = null; - return false; + return EvmExceptionType.OutOfGas; } returnData = vmState.Memory.Load(in position, in length).ToArray(); - return true; + return EvmExceptionType.None; } [SkipLocalsInit] - private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) + private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) where TTracing : struct, IIsTracing { Metrics.SelfDestructs++; Address inheritor = stack.PopAddress(); - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, spec, false)) return false; + if (inheritor is null) return EvmExceptionType.StackUnderflow; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, spec, false)) return EvmExceptionType.OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.CreateList.Contains(executingAccount); @@ -2355,13 +2390,13 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(EvmState vmState, ref EvmStack(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) + private (EvmExceptionType exceptionType, EvmState? callState) InstructionCreate(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec, Instruction instruction) where TTracing : struct, IIsTracing { ref readonly ExecutionEnvironment env = ref vmState.Env; @@ -2392,9 +2427,11 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack salt = default; if (instruction == Instruction.CREATE2) { @@ -2404,7 +2441,7 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack spec.MaxInitCodeSize) return (outOfGas: true, null); + if (initCodeLength > spec.MaxInitCodeSize) return (EvmExceptionType.OutOfGas, null); } long gasCost = GasCostOf.Create + @@ -2413,9 +2450,9 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients @@ -2423,7 +2460,7 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(); stack.PushZero(); - return (outOfGas: false, null); + return (EvmExceptionType.None, null); } Span initCode = vmState.Memory.LoadSpan(in memoryPositionOfInitCode, initCodeLength); @@ -2433,7 +2470,7 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(); stack.PushZero(); - return (outOfGas: false, null); + return (EvmExceptionType.None, null); } UInt256 accountNonce = _state.GetNonce(env.ExecutingAccount); @@ -2442,14 +2479,14 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(); stack.PushZero(); - return (outOfGas: false, null); + return (EvmExceptionType.None, null); } if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory?.Size ?? 0); // todo: === below is a new call - refactor / move long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; - if (!UpdateGas(callGas, ref gasAvailable)) return (outOfGas: true, null); + if (!UpdateGas(callGas, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); Address contractAddress = instruction == Instruction.CREATE ? ContractAddress.From(env.ExecutingAccount, _state.GetNonce(env.ExecutingAccount)) @@ -2473,7 +2510,7 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(); stack.PushZero(); - return (outOfGas: false, null); + return (EvmExceptionType.None, null); } if (accountExists) @@ -2521,20 +2558,20 @@ private bool InstructionSelfDestruct(EvmState vmState, ref EvmStack(EvmState vmState, ref EvmStack stack, ref long gasAvailable, Instruction instruction) + private static EvmExceptionType InstructionLog(EvmState vmState, ref EvmStack stack, ref long gasAvailable, Instruction instruction) where TTracing : struct, IIsTracing { - stack.PopUInt256(out UInt256 position); - stack.PopUInt256(out UInt256 length); + if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; + if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; long topicsCount = instruction - Instruction.LOG0; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return false; + if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; if (!UpdateGas( GasCostOf.Log + topicsCount * GasCostOf.LogTopic + - (long)length * GasCostOf.LogData, ref gasAvailable)) return false; + (long)length * GasCostOf.LogData, ref gasAvailable)) return EvmExceptionType.OutOfGas; ReadOnlyMemory data = vmState.Memory.Load(in position, length); Hash256[] topics = new Hash256[topicsCount]; @@ -2549,26 +2586,26 @@ private static bool InstructionLog(EvmState vmState, ref EvmStack(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) + private EvmExceptionType InstructionSStore(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing where TTracingRefunds : struct, IIsTracing where TTracingStorage : struct, IIsTracing { // fail fast before the first storage read if gas is not enough even for reset - if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return false; + if (!spec.UseNetGasMetering && !UpdateGas(spec.GetSStoreResetCost(), ref gasAvailable)) return EvmExceptionType.OutOfGas; if (spec.UseNetGasMeteringWithAStipendFix) { if (typeof(TTracingRefunds) == typeof(IsTracing)) _txTracer.ReportExtraGasPressure(GasCostOf.CallStipend - spec.GetNetMeteredSStoreCost() + 1); - if (gasAvailable <= GasCostOf.CallStipend) return false; + if (gasAvailable <= GasCostOf.CallStipend) return EvmExceptionType.OutOfGas; } - stack.PopUInt256(out UInt256 result); + if (!stack.PopUInt256(out UInt256 result)) return EvmExceptionType.StackUnderflow; Span bytes = stack.PopWord256(); bool newIsZero = bytes.IsZero(); if (!newIsZero) @@ -2587,7 +2624,7 @@ private bool InstructionSStore currentValue = _state.Get(in storageCell); // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); @@ -2608,14 +2645,14 @@ private bool InstructionSStore(long gasAvailable, EvmExceptionType exceptionType) @@ -2714,6 +2751,7 @@ private CallResult GetFailureReturn(long gasAvailable, Evm EvmExceptionType.InvalidSubroutineEntry => CallResult.InvalidSubroutineEntry, EvmExceptionType.InvalidSubroutineReturn => CallResult.InvalidSubroutineReturn, EvmExceptionType.StackOverflow => CallResult.StackOverflowException, + EvmExceptionType.StackUnderflow => CallResult.StackUnderflowException, EvmExceptionType.InvalidJumpDestination => CallResult.InvalidJumpDestination, EvmExceptionType.AccessViolation => CallResult.AccessViolationException, _ => throw new ArgumentOutOfRangeException(nameof(exceptionType), exceptionType, "")