diff --git a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs index c4bfadd34fa..ba71b881ae1 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Tracing/DebugTracerTests.cs @@ -303,7 +303,8 @@ public void Debugger_Can_Alter_Data_Stack(string bytecodeHex) { // we pop the condition and overwrite it with a false to force breaking out of the loop EvmStack stack = new(tracer.CurrentState.DataStackHead, tracer, tracer.CurrentState.DataStack); - if (!stack.PopLimbo()) throw new EvmStackUnderflowException(); + if (stack.Head == 0) throw new EvmStackUnderflowException(); + stack.PopLimbo(); stack.PushByte(0x00); tracer.MoveNext(); diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 6f9aaa10940..c23776fc909 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -43,12 +43,8 @@ public EvmStack(scoped in int head, ITxTracer txTracer, scoped in Span byt [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref byte PushBytesRef() { - // Workhorse method int head = Head; - if ((Head = head + 1) >= MaxStackSize) - { - ThrowEvmStackOverflowException(); - } + Head = head + 1; return ref Unsafe.Add(ref MemoryMarshal.GetReference(_bytes), head * WordSize); } @@ -70,7 +66,6 @@ public void PushBytes(scoped ReadOnlySpan value) if (value.Length != WordSize) { ref byte bytes = ref PushBytesRef(); - // Not full entry, clear first Unsafe.As(ref bytes) = default; value.CopyTo(MemoryMarshal.CreateSpan(ref Unsafe.Add(ref bytes, WordSize - value.Length), value.Length)); } @@ -197,6 +192,7 @@ public void Push20Bytes(ref byte value) ulong lane2 = Unsafe.As(ref Unsafe.Add(ref value, 4)); ulong lane3 = Unsafe.As(ref Unsafe.Add(ref value, 12)); + // Single 32-byte store head = Vector256.Create(default, lane1, lane2, lane3).AsByte(); } @@ -212,7 +208,6 @@ public void Push32Bytes(in Word value) if (TTracingInst.IsActive) _tracer.TraceWord(in value); - // Single 32-byte store PushedHead() = value; } @@ -262,7 +257,7 @@ public void PushZero() if (TTracingInst.IsActive) _tracer.ReportStackPush(Bytes.ZeroByteSpan); - // Single 32-byte store: Zero + // Single 32-byte store: Zero PushedHead() = default; } @@ -302,7 +297,6 @@ public unsafe void PushUInt64(ulong value) /// /// This method is a counterpart to and uses the same, raw data approach to write data back. /// - public void PushUInt256(in UInt256 value) where TTracingInst : struct, IFlag { @@ -359,14 +353,9 @@ public void PushSignedInt256(in Int256.Int256 value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool PopLimbo() + public void PopLimbo() { - if (Head-- == 0) - { - return false; - } - - return true; + Head--; } /// @@ -377,11 +366,10 @@ public bool PopLimbo() /// All it does is and then reverse endianness if needed. Then it creates . /// /// The returned value. - public bool PopUInt256(out UInt256 result) + public void PopUInt256(out UInt256 result) { Unsafe.SkipInit(out result); ref byte bytes = ref PopBytesByRef(); - if (Unsafe.IsNullRef(ref bytes)) return false; if (Avx2.IsSupported) { @@ -424,18 +412,11 @@ public bool PopUInt256(out UInt256 result) result = new UInt256(u0, u1, u2, u3); } - - return true; } public readonly bool PeekUInt256IsZero() { - int head = Head; - if (head-- == 0) - { - return false; - } - + int head = Head - 1; ref byte bytes = ref _bytes[head * WordSize]; return Unsafe.ReadUnaligned(ref bytes).IsZero; } @@ -443,87 +424,53 @@ public readonly bool PeekUInt256IsZero() [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly ref byte PeekBytesByRef() { - int head = Head; - if (head-- == 0) - { - return ref Unsafe.NullRef(); - } + int head = Head - 1; return ref Unsafe.Add(ref MemoryMarshal.GetReference(_bytes), head * WordSize); } public readonly Span PeekWord256() { - int head = Head; - if (head-- == 0) - { - ThrowEvmStackUnderflowException(); - } - + int head = Head - 1; return _bytes.Slice(head * WordSize, WordSize); } - public Address? PopAddress() => Head-- == 0 ? null : new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); + public Address? PopAddress() + => new Address(_bytes.Slice((--Head) * WordSize + WordSize - AddressSize, AddressSize).ToArray()); - public bool PopAddress(out Address address) + public void PopAddress(out Address address) { - if (Head-- == 0) - { - address = null; - return false; - } - + Head--; address = new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); - return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref byte PopBytesByRef() { - int head = Head; - if (head == 0) - { - return ref Unsafe.NullRef(); - } - head--; + int head = Head - 1; Head = head; return ref Unsafe.Add(ref MemoryMarshal.GetReference(_bytes), head * WordSize); } public Span PopWord256() - { - ref byte bytes = ref PopBytesByRef(); - if (Unsafe.IsNullRef(ref bytes)) ThrowEvmStackUnderflowException(); - - return MemoryMarshal.CreateSpan(ref bytes, WordSize); - } + => MemoryMarshal.CreateSpan(ref PopBytesByRef(), WordSize); - public bool PopWord256(out Span word) + public void PopWord256(out Span word) { - if (Head-- == 0) - { - word = default; - return false; - } - + Head--; word = _bytes.Slice(Head * WordSize, WordSize); - return true; } public byte PopByte() { ref byte bytes = ref PopBytesByRef(); - - if (Unsafe.IsNullRef(ref bytes)) ThrowEvmStackUnderflowException(); - return Unsafe.Add(ref bytes, WordSize - sizeof(byte)); } [SkipLocalsInit] - public EvmExceptionType Dup(int depth) + public void Dup(int depth) where TTracingInst : struct, IFlag { int head = Head; - if (head < depth) goto StackUnderflow; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); @@ -534,27 +481,17 @@ public EvmExceptionType Dup(int depth) if (TTracingInst.IsActive) Trace(depth); - if (++head >= MaxStackSize) goto StackOverflow; - - Head = head; - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; - StackOverflow: - return EvmExceptionType.StackOverflow; + Head = head + 1; } public readonly bool EnsureDepth(int depth) => Head >= depth; [SkipLocalsInit] - public readonly EvmExceptionType Swap(int depth) + public readonly void Swap(int depth) where TTracingInst : struct, IFlag { int head = Head; - if (head < depth) goto StackUnderflow; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); @@ -566,18 +503,12 @@ public readonly EvmExceptionType Swap(int depth) Unsafe.WriteUnaligned(ref top, buffer); if (TTracingInst.IsActive) Trace(depth); - - return EvmExceptionType.None; - // Jump forward to be unpredicted by the branch predictor. - StackUnderflow: - return EvmExceptionType.StackUnderflow; } - public readonly bool Exchange(int n, int m) + public readonly void Exchange(int n, int m) where TTracingInst : struct, IFlag { int maxDepth = Math.Max(n, m); - if (!EnsureDepth(maxDepth)) return false; ref byte bytes = ref MemoryMarshal.GetReference(_bytes); @@ -589,8 +520,6 @@ public readonly bool Exchange(int n, int m) Unsafe.WriteUnaligned(ref second, buffer); if (TTracingInst.IsActive) Trace(maxDepth); - - return true; } private readonly void Trace(int depth) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs index 7bffb4e634a..80e3fad7a7c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Bitwise.cs @@ -29,6 +29,11 @@ public interface IOpBitwise /// The second operand vector. /// The result of the bitwise operation. static abstract Word Operation(in Word a, in Word b); + + static virtual bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 2; + } } /// @@ -47,18 +52,19 @@ public static EvmExceptionType InstructionBitwise(Virtua where TGasPolicy : struct, IGasPolicy where TOpBitwise : struct, IOpBitwise { + if (TOpBitwise.CheckStackUnderflow(ref stack)) + goto StackUnderflow; + // Deduct the operation's gas cost. TGasPolicy.Consume(ref gas, TOpBitwise.GasCost); // Pop the first operand from the stack by reference to minimize copying. ref byte bytesRef = ref stack.PopBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; // Read the 256-bit vector from unaligned memory. Word aVec = ReadUnaligned(ref bytesRef); // Peek at the top of the stack for the second operand without removing it. bytesRef = ref stack.PeekBytesByRef(); - if (IsNullRef(ref bytesRef)) goto StackUnderflow; Word bVec = ReadUnaligned(ref bytesRef); // Write the result directly into the memory of the top stack element. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index e802d92416f..afd2ce56899 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -31,6 +31,11 @@ public interface IOpCall /// Returns the specific execution type of the call. /// abstract static ExecutionType ExecutionType { get; } + + /// + /// Checks the stack for underflow conditions specific to call operations. + /// + abstract static bool CheckStackUnderflow(ref EvmStack stack); } /// @@ -39,6 +44,11 @@ public interface IOpCall public struct OpCall : IOpCall { public static ExecutionType ExecutionType => ExecutionType.CALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 7; + } } /// @@ -47,6 +57,11 @@ public struct OpCall : IOpCall public struct OpCallCode : IOpCall { public static ExecutionType ExecutionType => ExecutionType.CALLCODE; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 7; + } } /// @@ -55,6 +70,11 @@ public struct OpCallCode : IOpCall public struct OpDelegateCall : IOpCall { public static ExecutionType ExecutionType => ExecutionType.DELEGATECALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 6; + } } /// @@ -64,6 +84,11 @@ public struct OpStaticCall : IOpCall { public static bool IsStatic => true; public static ExecutionType ExecutionType => ExecutionType.STATICCALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 6; + } } /// @@ -103,11 +128,15 @@ public static EvmExceptionType InstructionCall(VirtualMachine /// The virtual machine instance providing execution context. /// A read-only span of bytes containing the code. abstract static ReadOnlySpan GetCode(VirtualMachine vm); + + + /// + /// Checks the stack for underflow conditions specific to call operations. + /// + virtual static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 3; + } } /// @@ -60,11 +69,12 @@ public static EvmExceptionType InstructionCodeCopy where TTracingInst : struct, IFlag { + if (TOpCodeCopy.CheckStackUnderflow(ref stack)) goto StackUnderflow; + // Pop destination offset, source offset, and copy length. - if (!stack.PopUInt256(out UInt256 a) || - !stack.PopUInt256(out UInt256 b) || - !stack.PopUInt256(out UInt256 result)) - goto StackUnderflow; + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); + stack.PopUInt256(out UInt256 result); // Deduct gas for the operation plus the cost for memory expansion. // Gas cost is calculated as a fixed "VeryLow" cost plus a per-32-bytes cost. @@ -143,14 +153,15 @@ public static EvmExceptionType InstructionExtCodeCopy( where TTracingInst : struct, IFlag { IReleaseSpec spec = vm.Spec; + + if (CheckStackUnderflow(ref stack, 4)) goto StackUnderflow; + // Retrieve the target account address. Address address = stack.PopAddress(); // Pop destination offset, source offset, and length from the stack. - if (address is null || - !stack.PopUInt256(out UInt256 a) || - !stack.PopUInt256(out UInt256 b) || - !stack.PopUInt256(out UInt256 result)) - goto StackUnderflow; + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); + stack.PopUInt256(out UInt256 result); // Deduct gas cost: cost for external code access plus memory expansion cost. TGasPolicy.ConsumeDataCopyGas(ref gas, isExternalCode: true, spec.GetExtCodeCost(), GasCostOf.Memory * EvmCalculations.Div32Ceiling(in result, out bool outOfGas)); @@ -229,13 +240,14 @@ public static EvmExceptionType InstructionExtCodeSize( where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; + IReleaseSpec spec = vm.Spec; // Deduct the gas cost for external code access. TGasPolicy.Consume(ref gas, spec.GetExtCodeCost()); // Pop the account address from the stack. Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; // Charge gas for accessing the account's state. if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vm.VmState.AccessTracker, vm.TxTracer.IsTracingAccess, address)) @@ -255,12 +267,19 @@ public static EvmExceptionType InstructionExtCodeSize( } // If the next instruction is GT or EQ and the top stack element is zero, // then this pattern likely corresponds to a contract existence check. - else if ((nextInstruction == Instruction.GT || nextInstruction == Instruction.EQ) && - stack.PeekUInt256IsZero()) + else if ((nextInstruction == Instruction.GT || nextInstruction == Instruction.EQ)) { - optimizeAccess = true; - // Remove the zero from the stack since we will have consumed it. - if (!stack.PopLimbo()) goto StackUnderflow; + if (CheckStackUnderflow(ref stack, 1)) + { + goto StackUnderflow; + } + + if (stack.PeekUInt256IsZero()) + { + optimizeAccess = true; + // Remove the zero from the stack since we will have consumed it. + stack.PopLimbo(); + } } if (optimizeAccess) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs index e8d9e1adbab..4ac7ab40b3c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.ControlFlow.cs @@ -31,6 +31,8 @@ public static EvmExceptionType InstructionProgramCounter where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Deduct the base gas cost for reading the program counter. TGasPolicy.Consume(ref gas, GasCostOf.Base); // The program counter pushed is adjusted by -1 to reflect the correct opcode location. @@ -77,10 +79,13 @@ public static EvmExceptionType InstructionJumpDest(VirtualMachine(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy { + + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; + // Deduct the gas cost for performing a jump. TGasPolicy.Consume(ref gas, GasCostOf.Jump); // Pop the jump destination from the stack. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Validate the jump destination and update the program counter if valid. if (!Jump(result, ref programCounter, vm.VmState.Env)) goto InvalidJumpDestination; @@ -110,13 +115,14 @@ public static EvmExceptionType InstructionJump(VirtualMachine(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy { + if (CheckStackUnderflow(ref stack, 2)) goto StackUnderflow; + // Deduct the high gas cost for a conditional jump. TGasPolicy.Consume(ref gas, GasCostOf.JumpI); // Pop the jump destination. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); - bool shouldJump = TestJumpCondition(ref stack, out bool isOverflow); - if (isOverflow) goto StackUnderflow; + bool shouldJump = TestJumpCondition(ref stack); if (shouldJump) { if (!Jump(result, ref programCounter, vm.VmState.Env)) goto InvalidJumpDestination; @@ -132,16 +138,10 @@ public static EvmExceptionType InstructionJumpIf(VirtualMachine>(ref condition) != default); } @@ -172,12 +172,11 @@ public static EvmExceptionType InstructionStop(VirtualMachine(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy { + if (CheckStackUnderflow(ref stack, 2)) goto StackUnderflow; + // Attempt to pop memory offset and length; if either fails, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 position) || - !stack.PopUInt256(out UInt256 length)) - { - goto StackUnderflow; - } + stack.PopUInt256(out UInt256 position); + stack.PopUInt256(out UInt256 length); // Ensure sufficient gas for any required memory expansion. if (!TGasPolicy.UpdateMemoryCost(ref gas, in position, in length, vm.VmState) || @@ -208,6 +207,8 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachi // Increment metrics for self-destruct operations. Metrics.IncrementSelfDestructs(); + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; + VmState vmState = vm.VmState; IReleaseSpec spec = vm.Spec; IWorldState state = vm.WorldState; @@ -224,8 +225,6 @@ private static EvmExceptionType InstructionSelfDestruct(VirtualMachi // Pop the inheritor address from the stack; signal underflow if missing. Address inheritor = stack.PopAddress(); - if (inheritor is null) - goto StackUnderflow; // Charge gas for account access; if insufficient, signal out-of-gas. if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vmState.AccessTracker, vm.TxTracer.IsTracingAccess, inheritor, false)) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs index 66f79084b2c..f353578e86c 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs @@ -30,6 +30,11 @@ public interface IOpCreate /// Gets the execution type corresponding to the create operation. /// abstract static ExecutionType ExecutionType { get; } + + /// + /// Checks the stack for underflow conditions specific to call operations. + /// + abstract static bool CheckStackUnderflow(ref EvmStack stack); } /// @@ -41,6 +46,11 @@ public struct OpCreate : IOpCreate /// Gets the execution type for the CREATE opcode. /// public static ExecutionType ExecutionType => ExecutionType.CREATE; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 3; + } } /// @@ -52,6 +62,11 @@ public struct OpCreate2 : IOpCreate /// Gets the execution type for the CREATE2 opcode. /// public static ExecutionType ExecutionType => ExecutionType.CREATE2; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 4; + } } /// @@ -80,6 +95,9 @@ public static EvmExceptionType InstructionCreate salt = default; // For CREATE2, an extra salt value is required. Use type check to differentiate. diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs index f0015a84489..b1de9caa0d5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Crypto.cs @@ -23,9 +23,11 @@ public static EvmExceptionType InstructionKeccak256(Vi where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 2)) goto StackUnderflow; + // Ensure two 256-bit words are available (memory offset and length). - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) - goto StackUnderflow; + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); // Deduct gas: base cost plus additional cost per 32-byte word. TGasPolicy.Consume(ref gas, GasCostOf.Sha3 + GasCostOf.Sha3Word * EvmCalculations.Div32Ceiling(in b, out bool outOfGas)); diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 7e770d5ec07..b727e983959 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -168,6 +168,8 @@ public static EvmExceptionType InstructionEnvAddress where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Deduct the gas cost as defined by the operation implementation. TGasPolicy.Consume(ref gas, TOpEnv.GasCost); @@ -197,6 +199,8 @@ public static EvmExceptionType InstructionBlkAddress where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Deduct the gas cost as defined by the operation implementation. TGasPolicy.Consume(ref gas, TOpEnv.GasCost); @@ -225,6 +229,8 @@ public static EvmExceptionType InstructionEnvUInt256 where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); ref readonly UInt256 result = ref TOpEnv.Operation(vm.VmState); @@ -250,6 +256,8 @@ public static EvmExceptionType InstructionBlkUInt256 where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); ref readonly UInt256 result = ref TOpEnv.Operation(vm); @@ -275,6 +283,8 @@ public static EvmExceptionType InstructionEnvUInt32 where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); uint result = TOpEnv.Operation(vm.VmState); @@ -300,6 +310,8 @@ public static EvmExceptionType InstructionEnvUInt64 where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); ulong result = TOpEnv.Operation(vm.VmState); @@ -325,6 +337,8 @@ public static EvmExceptionType InstructionBlkUInt64 where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); ulong result = TOpEnv.Operation(vm); @@ -350,6 +364,8 @@ public static EvmExceptionType InstructionEnv32Bytes where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, TOpEnv.GasCost); ref readonly ValueHash256 result = ref TOpEnv.Operation(vm); @@ -450,6 +466,7 @@ public static EvmExceptionType InstructionBlobBaseFee( // If the blob base fee is missing (no ExcessBlobGas set), this opcode is invalid. if (!context.Header.ExcessBlobGas.HasValue) goto BadInstruction; + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; // Charge the base gas cost for this opcode. TGasPolicy.Consume(ref gas, GasCostOf.Base); stack.Push32Bytes(in context.BlobBaseFee); @@ -549,12 +566,13 @@ public static EvmExceptionType InstructionBalance(Virt where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; + IReleaseSpec spec = vm.Spec; // Deduct gas cost for balance operation as per specification. TGasPolicy.Consume(ref gas, spec.GetBalanceCost()); Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; // Charge gas for account access. If insufficient gas remains, abort. if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vm.VmState.AccessTracker, vm.TxTracer.IsTracingAccess, address)) goto OutOfGas; @@ -586,6 +604,8 @@ public static EvmExceptionType InstructionSelfBalance( where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + TGasPolicy.Consume(ref gas, GasCostOf.SelfBalance); // Get balance for currently executing account. @@ -614,11 +634,12 @@ public static EvmExceptionType InstructionExtCodeHash( where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; + IReleaseSpec spec = vm.Spec; TGasPolicy.Consume(ref gas, spec.GetExtCodeHashCost()); Address address = stack.PopAddress(); - if (address is null) goto StackUnderflow; // Check if enough gas for account access and charge accordingly. if (!TGasPolicy.ConsumeAccountAccessGas(ref gas, spec, in vm.VmState.AccessTracker, vm.TxTracer.IsTracingAccess, address)) goto OutOfGas; @@ -663,10 +684,11 @@ public static EvmExceptionType InstructionExtCodeHashEof(V where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Charge the base gas cost for this opcode. TGasPolicy.Consume(ref gas, GasCostOf.Base); stack.Push32Bytes(in vm.BlockExecutionContext.PrevRandao); @@ -737,6 +761,7 @@ public static EvmExceptionType InstructionGas(VirtualM where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; // Deduct the base gas cost for reading gas. TGasPolicy.Consume(ref gas, GasCostOf.Base); @@ -771,11 +796,12 @@ public static EvmExceptionType InstructionBlobHash(Vir where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; // Deduct the gas cost for blob hash operation. TGasPolicy.Consume(ref gas, GasCostOf.BlobHash); // Pop the blob index from the stack. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Retrieve the array of versioned blob hashes from the execution context. byte[][] versionedHashes = vm.TxExecutionContext.BlobVersionedHashes; @@ -817,11 +843,12 @@ public static EvmExceptionType InstructionBlockHash(Vi where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 1)) goto StackUnderflow; // Deduct the gas cost for block hash operation. TGasPolicy.Consume(ref gas, GasCostOf.BlockHash); // Pop the block number from the stack. - if (!stack.PopUInt256(out UInt256 a)) goto StackUnderflow; + stack.PopUInt256(out UInt256 a); // Convert the block number to a long. Clamp the value to long.MaxValue if it exceeds it. long number = a > long.MaxValue ? long.MaxValue : (long)a.u0; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs index 3be4898dfb1..40822b5d53f 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Eof.cs @@ -29,6 +29,7 @@ public interface IOpEofCall virtual static bool IsStatic => false; // Specifies the execution type of the call. abstract static ExecutionType ExecutionType { get; } + abstract static bool CheckStackUnderflow(ref EvmStack stack); } /// @@ -37,6 +38,11 @@ public interface IOpEofCall public struct OpEofCall : IOpEofCall { public static ExecutionType ExecutionType => ExecutionType.EOFCALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 4; + } } /// @@ -45,6 +51,11 @@ public struct OpEofCall : IOpEofCall public struct OpEofDelegateCall : IOpEofCall { public static ExecutionType ExecutionType => ExecutionType.EOFDELEGATECALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 3; + } } /// @@ -54,6 +65,11 @@ public struct OpEofStaticCall : IOpEofCall { public static bool IsStatic => true; public static ExecutionType ExecutionType => ExecutionType.EOFSTATICCALL; + + public static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 3; + } } /// @@ -72,6 +88,8 @@ public static EvmExceptionType InstructionReturnDataSize where TTracingInst : struct, IFlag { + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Deduct base gas cost for this instruction. TGasPolicy.Consume(ref gas, GasCostOf.Base); @@ -99,13 +117,11 @@ public static EvmExceptionType InstructionReturnDataCopy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 3)) goto StackUnderflow; // Pop the required parameters: destination memory offset, source offset in return data, and number of bytes to copy. - if (!stack.PopUInt256(out UInt256 destOffset) || - !stack.PopUInt256(out UInt256 sourceOffset) || - !stack.PopUInt256(out UInt256 size)) - { - goto StackUnderflow; - } + stack.PopUInt256(out UInt256 destOffset); + stack.PopUInt256(out UInt256 sourceOffset); + stack.PopUInt256(out UInt256 size); // Deduct the fixed gas cost and the memory cost based on the size (rounded up to 32-byte words). TGasPolicy.Consume(ref gas, GasCostOf.VeryLow + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in size, out bool outOfGas)); @@ -161,11 +177,13 @@ public static EvmExceptionType InstructionDataLoad(Vir where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + ICodeInfo codeInfo = vm.VmState.Env.CodeInfo; // Ensure the instruction is only valid for non-legacy (EOF) code. if (codeInfo.Version == 0) goto BadInstruction; + if (CheckStackUnderflow(ref stack, 1)) return EvmExceptionType.StackUnderflow; // Deduct gas required for data loading. if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataLoad)) goto OutOfGas; @@ -193,10 +211,13 @@ public static EvmExceptionType InstructionDataLoadN(Vi where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + ICodeInfo codeInfo = vm.VmState.Env.CodeInfo; if (codeInfo.Version == 0) goto BadInstruction; + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataLoadN)) goto OutOfGas; @@ -255,13 +276,12 @@ public static EvmExceptionType InstructionDataCopy(Vir if (codeInfo.Version == 0) goto BadInstruction; + if (CheckStackUnderflow(ref stack, 3)) goto StackUnderflow; + // Pop destination memory offset, data section offset, and size. - if (!stack.PopUInt256(out UInt256 memOffset) || - !stack.PopUInt256(out UInt256 offset) || - !stack.PopUInt256(out UInt256 size)) - { - goto StackUnderflow; - } + stack.PopUInt256(out UInt256 memOffset); + stack.PopUInt256(out UInt256 offset); + stack.PopUInt256(out UInt256 size); // Calculate memory expansion gas cost and deduct overall gas for data copy. if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.DataCopy + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in size, out bool outOfGas)) @@ -539,12 +559,14 @@ public static EvmExceptionType InstructionDupN(Virtual // Read the immediate operand. int imm = codeInfo.CodeSection.Span[programCounter]; - // Duplicate the (imm+1)th stack element. - EvmExceptionType result = stack.Dup(imm + 1); + if (CheckStackUnderflow(ref stack, imm + 1)) return EvmExceptionType.StackUnderflow; + if (CheckStackOverflow(ref stack, 1)) return EvmExceptionType.StackOverflow; + // Duplicate the (imm+1)th stack element. + stack.Dup(imm + 1); programCounter += 1; - return result; + return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; @@ -570,11 +592,14 @@ public static EvmExceptionType InstructionSwapN(Virtua // Immediate operand determines the swap index. int n = 1 + (int)codeInfo.CodeSection.Span[programCounter]; - EvmExceptionType result = stack.Swap(n + 1); + + if (CheckStackUnderflow(ref stack, n + 1)) return EvmExceptionType.StackUnderflow; + + stack.Swap(n + 1); programCounter += 1; - return result; + return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor. OutOfGas: return EvmExceptionType.OutOfGas; @@ -640,6 +665,8 @@ public static EvmExceptionType InstructionEofCreate(Vi if (vm.VmState.IsStatic) goto StaticCallViolation; + if (CheckStackUnderflow(ref stack, 4)) return EvmExceptionType.StackUnderflow; + // Cast the current code info to EOF-specific container type. EofCodeInfo container = env.CodeInfo as EofCodeInfo; ExecutionType currentContext = ExecutionType.EOFCREATE; @@ -653,13 +680,10 @@ public static EvmExceptionType InstructionEofCreate(Vi int initContainerIndex = codeSection[programCounter++]; // 3. Pop contract creation parameters from the stack. - if (!stack.PopUInt256(out UInt256 value) || - !stack.PopWord256(out Span salt) || - !stack.PopUInt256(out UInt256 dataOffset) || - !stack.PopUInt256(out UInt256 dataSize)) - { - goto OutOfGas; - } + stack.PopUInt256(out UInt256 value); + stack.PopWord256(out Span salt); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataSize); // 4. Charge for memory expansion for the input data. if (!TGasPolicy.UpdateMemoryCost(ref gas, in dataOffset, dataSize, vm.VmState)) @@ -795,6 +819,8 @@ public static EvmExceptionType InstructionReturnCode(VirtualMachine< if (!TGasPolicy.UpdateGas(ref gas, GasCostOf.ReturnCode)) goto OutOfGas; + if (CheckStackUnderflow(ref stack, 2)) return EvmExceptionType.StackUnderflow; + IReleaseSpec spec = vm.Spec; EofCodeInfo codeInfo = (EofCodeInfo)vm.VmState.Env.CodeInfo; @@ -847,10 +873,11 @@ public static EvmExceptionType InstructionReturnDataLoad(slice); @@ -893,13 +920,13 @@ public static EvmExceptionType InstructionEofCall targetBytes) || - !stack.PopUInt256(out UInt256 dataOffset) || - !stack.PopUInt256(out UInt256 dataLength)) - { + if (TOpEofCall.CheckStackUnderflow(ref stack)) goto StackUnderflow; - } + + // 1. Pop the target address (as 32 bytes) and memory offsets/length for the call data. + stack.PopWord256(out Span targetBytes); + stack.PopUInt256(out UInt256 dataOffset); + stack.PopUInt256(out UInt256 dataLength); UInt256 transferValue; UInt256 callValue; @@ -914,13 +941,10 @@ public static EvmExceptionType InstructionEofCallThe input 256‐bit vector. /// The result of the operation as a 256‐bit vector. abstract static Word Operation(Word value); + + virtual static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 1; + } } /// @@ -56,6 +61,9 @@ public static EvmExceptionType InstructionMath1Param(Virtua where TGasPolicy : struct, IGasPolicy where TOpMath : struct, IOpMath1Param { + if (TOpMath.CheckStackUnderflow(ref stack)) + goto StackUnderflow; + // Deduct the gas cost associated with the math operation. TGasPolicy.Consume(ref gas, TOpMath.GasCost); @@ -117,11 +125,13 @@ public static EvmExceptionType InstructionByte(Virtual where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, 2)) + goto StackUnderflow; + TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Pop the byte position and the 256-bit word. - if (!stack.PopUInt256(out UInt256 a)) - goto StackUnderflow; + stack.PopUInt256(out UInt256 a); Span bytes = stack.PopWord256(); // If the position is out-of-range, push zero. @@ -157,16 +167,15 @@ public static EvmExceptionType InstructionByte(Virtual public static EvmExceptionType InstructionSignExtend(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy { + if (CheckStackUnderflow(ref stack, 2)) + goto StackUnderflow; + TGasPolicy.Consume(ref gas, GasCostOf.Low); // Pop the index to determine which byte to use for sign extension. - if (!stack.PopUInt256(out UInt256 a)) - goto StackUnderflow; + stack.PopUInt256(out UInt256 a); if (a >= BigInt32) { - // If the index is out-of-range, no extension is needed. - if (!stack.EnsureDepth(1)) - goto StackUnderflow; return EvmExceptionType.None; } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs index 28c59de7791..426b2faf1e2 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math2Param.cs @@ -31,6 +31,14 @@ public interface IOpMath2Param /// The second operand. /// The result of the operation. abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); + + /// + /// Checks the stack for underflow conditions specific to call operations. + /// + virtual static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 2; + } } /// @@ -54,11 +62,17 @@ public static EvmExceptionType InstructionMath2Param(VirtualM where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { - // Charge the fixed gas cost for exponentiation. - TGasPolicy.Consume(ref gas, GasCostOf.Exp); - - // Pop the base value and exponent from the stack. - if (!stack.PopUInt256(out UInt256 a) || - !stack.PopUInt256(out UInt256 exponent)) + if (CheckStackUnderflow(ref stack, 2)) { goto StackUnderflow; } + // Charge the fixed gas cost for exponentiation. + TGasPolicy.Consume(ref gas, GasCostOf.Exp); + + // Pop the base value from the stack. + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 exponent); + // Determine the effective byte-length of the exponent. int leadingZeros = exponent.CountLeadingZeros() >> 3; if (leadingZeros == 32) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs index f9600b8dde3..c86363e539b 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math3Param.cs @@ -15,6 +15,10 @@ public interface IOpMath3Param { virtual static long GasCost => GasCostOf.Mid; abstract static void Operation(in UInt256 a, in UInt256 b, in UInt256 c, out UInt256 result); + virtual static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 3; + } } [SkipLocalsInit] @@ -23,9 +27,16 @@ public static EvmExceptionType InstructionMath3ParamThe value to be shifted. /// The resulting shifted value. abstract static void Operation(in UInt256 a, in UInt256 b, out UInt256 result); + + /// + /// Checks the stack for underflow conditions specific to call operations. + /// + virtual static bool CheckStackUnderflow(ref EvmStack stack) + { + return stack.Head < 2; + } } /// @@ -55,23 +63,25 @@ public static EvmExceptionType InstructionShift= 256) { // Pop the second operand without using its value. - if (!stack.PopLimbo()) goto StackUnderflow; + stack.PopLimbo(); stack.PushZero(); } else { // Otherwise, pop the value to be shifted. - if (!stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + stack.PopUInt256(out UInt256 b); // Perform the shift operation using the specific implementation. TOpShift.Operation(in a, in b, out UInt256 result); stack.PushUInt256(in result); @@ -102,11 +112,19 @@ public static EvmExceptionType InstructionSar(VirtualM where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + static bool CheckStackUnderflow(ref EvmStack stack) => stack.Head < 2; + + if (CheckStackUnderflow(ref stack)) + { + goto StackUnderflow; + } + // Deduct the gas cost for the arithmetic shift operation. TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Pop the shift amount and the value to be shifted. - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b)) goto StackUnderflow; + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); // If the shift amount is 256 or more, the result depends solely on the sign of the value. if (a >= 256) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs index ebe50e55f70..2018ec4eb41 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs @@ -18,6 +18,21 @@ namespace Nethermind.Evm; internal static partial class EvmInstructions { + const int PopStackRequiredItemsCount = 1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckStackUnderflow(ref EvmStack stack, int itemsPoppedCount) + { + return stack.Head < itemsPoppedCount; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckStackOverflow(ref EvmStack stack, int itemsPushedCount) + { + return stack.Head + itemsPushedCount >= EvmStack.MaxStackSize; + } + + /// /// Pops a value from the EVM stack. /// Deducts the base gas cost and returns an exception if the stack is underflowed. @@ -32,10 +47,15 @@ internal static partial class EvmInstructions public static EvmExceptionType InstructionPop(VirtualMachine vm, ref EvmStack stack, ref TGasPolicy gas, ref int programCounter) where TGasPolicy : struct, IGasPolicy { + if (CheckStackUnderflow(ref stack, PopStackRequiredItemsCount)) + return EvmExceptionType.StackUnderflow; + // Deduct the minimal gas cost for a POP operation. TGasPolicy.Consume(ref gas, GasCostOf.Base); // Pop from the stack; if nothing to pop, signal a stack underflow. - return stack.PopLimbo() ? EvmExceptionType.None : EvmExceptionType.StackUnderflow; + stack.PopLimbo(); + + return EvmExceptionType.None; } /// @@ -121,6 +141,12 @@ public static EvmExceptionType InstructionPush2(Virtua where TTracingInst : struct, IFlag { const int Size = sizeof(ushort); + + if (CheckStackOverflow(ref stack, 1)) + { + return EvmExceptionType.StackOverflow; + } + // Deduct a very low gas cost for the push operation. TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Retrieve the code segment containing immediate data. @@ -150,9 +176,13 @@ public static EvmExceptionType InstructionPush2(Virtua else { TGasPolicy.Consume(ref gas, GasCostOf.JumpI); + if (CheckStackUnderflow(ref stack, 1)) + { + goto StackUnderflow; + } + vm.OpCodeCount++; - bool shouldJump = TestJumpCondition(ref stack, out bool isOverflow); - if (isOverflow) goto StackUnderflow; + bool shouldJump = TestJumpCondition(ref stack); if (!shouldJump) { // Move forward by 2 bytes + JUMPI @@ -493,6 +523,12 @@ public static EvmExceptionType InstructionPush0(Virtua where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + + if (CheckStackOverflow(ref stack, 1)) + { + return EvmExceptionType.StackOverflow; + } + TGasPolicy.Consume(ref gas, GasCostOf.Base); stack.PushZero(); return EvmExceptionType.None; @@ -516,6 +552,11 @@ public static EvmExceptionType InstructionPush(TOpCount.Count); + stack.Dup(TOpCount.Count); + + return EvmExceptionType.None; } /// @@ -566,9 +615,16 @@ public static EvmExceptionType InstructionSwap(TOpCount.Count + 1); + stack.Swap(TOpCount.Count + 1); + + return EvmExceptionType.None; } /// @@ -595,8 +651,12 @@ public static EvmExceptionType InstructionLog(VirtualMachi // Logging is not permitted in static call contexts. if (vmState.IsStatic) goto StaticCallViolation; + if (CheckStackUnderflow(ref stack, TOpCount.Count + 2)) + goto StackUnderflow; + // Pop memory offset and length for the log data. - if (!stack.PopUInt256(out UInt256 position) || !stack.PopUInt256(out UInt256 length)) goto StackUnderflow; + stack.PopUInt256(out UInt256 position); + stack.PopUInt256(out UInt256 length); // The number of topics is defined by the generic parameter. long topicsCount = TOpCount.Count; diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs index 1cd5b0f56ec..3631bd03f62 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs @@ -18,6 +18,18 @@ namespace Nethermind.Evm; /// internal static partial class EvmInstructions { + const int TStoreStackRequiredItems = 2; + const int TLoadStackRequiredItems = 1; + + const int SStoreStackRequiredItems = 2; + const int SLoadStackRequiredItems = 1; + + const int MStoreStackRequiredItems = 2; + const int MStore8StackRequiredItems = 2; + const int MLoadStackRequiredItems = 1; + const int MCopyStackRequiredItems = 3; + + const int CallDataLoadStackRequiredItems = 1; /// /// Executes the transient load (TLOAD) instruction. @@ -36,14 +48,18 @@ public static EvmExceptionType InstructionTLoad(Virtua where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + // Increment the opcode metric for TLOAD. Metrics.TloadOpcode++; + if (CheckStackUnderflow(ref stack, TLoadStackRequiredItems)) + goto StackUnderflow; + // Deduct the fixed gas cost for TLOAD. TGasPolicy.Consume(ref gas, GasCostOf.TLoad); // Attempt to pop the key (offset) from the stack; if unavailable, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Construct a transient storage cell using the executing account and the provided offset. StorageCell storageCell = new(vm.VmState.Env.ExecutingAccount, in result); @@ -88,6 +104,9 @@ public static EvmExceptionType InstructionTStore(VirtualMachine vmState = vm.VmState; // Disallow storage modification during static calls. @@ -97,7 +116,7 @@ public static EvmExceptionType InstructionTStore(VirtualMachine(Virtu where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, MStoreStackRequiredItems)) + goto StackUnderflow; + TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Pop the memory offset; if not available, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Retrieve the 32-byte word to be stored. Span bytes = stack.PopWord256(); @@ -190,10 +212,13 @@ public static EvmExceptionType InstructionMStore8(Virt where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, MStore8StackRequiredItems)) + goto StackUnderflow; + TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Pop the memory offset from the stack; if missing, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Pop a single byte from the stack. byte data = stack.PopByte(); @@ -237,10 +262,13 @@ public static EvmExceptionType InstructionMLoad(Virtua where TGasPolicy : struct, IGasPolicy where TTracingInst : struct, IFlag { + if (CheckStackUnderflow(ref stack, MLoadStackRequiredItems)) + goto StackUnderflow; + TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); // Pop the memory offset; if missing, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); VmState vmState = vm.VmState; @@ -287,8 +315,13 @@ public static EvmExceptionType InstructionMCopy(Virtua // Increment the opcode metric for MCOPY. Metrics.MCopyOpcode++; + if (CheckStackUnderflow(ref stack, MCopyStackRequiredItems)) + goto StackUnderflow; + // Pop destination, source, and length values; if any are missing, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 a) || !stack.PopUInt256(out UInt256 b) || !stack.PopUInt256(out UInt256 c)) goto StackUnderflow; + stack.PopUInt256(out UInt256 a); + stack.PopUInt256(out UInt256 b); + stack.PopUInt256(out UInt256 c); // Calculate additional gas cost based on the length (using a division rounding-up method) and deduct the total cost. TGasPolicy.Consume(ref gas, GasCostOf.VeryLow + GasCostOf.VeryLow * EvmCalculations.Div32Ceiling(c, out bool outOfGas)); @@ -344,6 +377,9 @@ internal static EvmExceptionType InstructionSStoreUnmetered vmState = vm.VmState; // Disallow storage modifications in static calls. if (vmState.IsStatic) goto StaticCallViolation; @@ -355,7 +391,7 @@ internal static EvmExceptionType InstructionSStoreUnmetered bytes = stack.PopWord256(); // Determine if the new value is effectively zero and normalize non-zero values by stripping leading zeros. @@ -447,6 +483,9 @@ internal static EvmExceptionType InstructionSStoreMetered vmState = vm.VmState; // Disallow storage modifications in static calls. if (vmState.IsStatic) goto StaticCallViolation; @@ -463,7 +502,7 @@ internal static EvmExceptionType InstructionSStoreMetered bytes = stack.PopWord256(); // Determine if the new value is effectively zero and normalize non-zero values by stripping leading zeros. @@ -617,11 +656,14 @@ internal static EvmExceptionType InstructionSLoad(Virt // Increment the SLOAD opcode metric. Metrics.IncrementSLoadOpcode(); + if (CheckStackUnderflow(ref stack, SLoadStackRequiredItems)) + goto StackUnderflow; + // Deduct the gas cost for performing an SLOAD. TGasPolicy.Consume(ref gas, spec.GetSLoadCost()); // Pop the key from the stack; if unavailable, signal a stack underflow. - if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow; + stack.PopUInt256(out UInt256 result); // Construct the storage cell for the executing account. Address executingAccount = vm.VmState.Env.ExecutingAccount; @@ -661,9 +703,11 @@ public static EvmExceptionType InstructionCallDataLoad { TGasPolicy.Consume(ref gas, GasCostOf.VeryLow); - // Pop the offset from which to load call data. - if (!stack.PopUInt256(out UInt256 result)) + if (CheckStackUnderflow(ref stack, CallDataLoadStackRequiredItems)) goto StackUnderflow; + + // Pop the offset from which to load call data. + stack.PopUInt256(out UInt256 result); // Load 32 bytes from input data, applying zero padding as needed. stack.PushBytes(vm.VmState.Env.InputData.SliceWithZeroPadding(result, 32));