diff --git a/src/compiler/evm_frontend/evm_imported.cpp b/src/compiler/evm_frontend/evm_imported.cpp index 5292db6e7..166b26c8f 100644 --- a/src/compiler/evm_frontend/evm_imported.cpp +++ b/src/compiler/evm_frontend/evm_imported.cpp @@ -135,8 +135,7 @@ bool resolveDelegatedCallCodeAddress(zen::runtime::EVMInstance *Instance, Module->Host->access_account(CodeAddr) == EVMC_ACCESS_COLD ? zen::evm::COLD_ACCOUNT_ACCESS_COST : zen::evm::WARM_STORAGE_READ_COST; - Instance->chargeGas(DelegateAccessCost); - return true; + return Instance->chargeGas(DelegateAccessCost); } } // namespace @@ -174,6 +173,7 @@ const RuntimeFunctions &getRuntimeFunctionTable() { .GetBlobHash = &evmGetBlobHash, .GetBlobBaseFee = &evmGetBlobBaseFee, .GetSLoad = &evmGetSLoad, + .GetErrorCode = &evmGetErrorCode, .SetSStore = &evmSetSStore, .GetGas = &evmGetGas, .GetTLoad = &evmGetTLoad, @@ -305,7 +305,9 @@ const intx::uint256 *evmGetExp(zen::runtime::EVMInstance *Instance, const uint64_t GasPerByte = Rev < EVMC_SPURIOUS_DRAGON ? zen::evm::EXP_BYTE_GAS_PRE_SPURIOUS_DRAGON : zen::evm::EXP_BYTE_GAS; - Instance->chargeGas(ExponentByteSize * GasPerByte); + if (!Instance->chargeGas(ExponentByteSize * GasPerByte)) { + return storeUint256Result(intx::uint256{0}); + } // EVM: (Base ^ Exponent) % (2^256) return storeUint256Result(intx::exp(Base, Exponent)); @@ -330,7 +332,9 @@ const intx::uint256 *evmGetBalance(zen::runtime::EVMInstance *Instance, evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_BERLIN && Module->Host->access_account(Addr) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST)) { + return storeUint256Result(intx::uint256{0}); + } } evmc::bytes32 BalanceBytes = Module->Host->get_balance(Addr); @@ -406,7 +410,9 @@ uint64_t evmGetExtCodeSize(zen::runtime::EVMInstance *Instance, evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_BERLIN && Module->Host->access_account(Addr) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST)) { + return 0; + } } uint64_t Size = Module->Host->get_code_size(Addr); @@ -423,7 +429,9 @@ const intx::uint256 *evmGetExtCodeHash(zen::runtime::EVMInstance *Instance, evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_BERLIN && Module->Host->access_account(Addr) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST)) { + return storeUint256Result(intx::uint256{0}); + } } evmc::bytes32 Hash = Module->Host->get_code_hash(Addr); @@ -597,7 +605,9 @@ void evmSetCallDataCopy(zen::runtime::EVMInstance *Instance, return; } if (uint64_t CopyGas = calculateWordCopyGas(Size)) { - Instance->chargeGas(CopyGas); + if (!Instance->chargeGas(CopyGas)) { + return; + } } const evmc_message *Msg = Instance->getCurrentMessage(); @@ -637,7 +647,9 @@ void evmSetExtCodeCopy(zen::runtime::EVMInstance *Instance, evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_BERLIN && Module->Host->access_account(Addr) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST)) { + return; + } } if (!Instance->expandMemoryChecked(DestOffset, Size)) { @@ -645,7 +657,9 @@ void evmSetExtCodeCopy(zen::runtime::EVMInstance *Instance, } if (uint64_t CopyGas = calculateWordCopyGas(Size)) { - Instance->chargeGas(CopyGas); + if (!Instance->chargeGas(CopyGas)) { + return; + } } // When Size is 0, no memory operations are needed @@ -694,7 +708,9 @@ uint64_t evmSetReturnDataCopy(zen::runtime::EVMInstance *Instance, return 0; } if (uint64_t CopyGas = calculateWordCopyGas(Size)) { - Instance->chargeGas(CopyGas); + if (!Instance->chargeGas(CopyGas)) { + return 0; + } } uint8_t *MemoryBase = Instance->getMemoryBase(); @@ -736,7 +752,9 @@ static void evmEmitLogGeneric(zen::runtime::EVMInstance *Instance, } const uint64_t LogDataCost = 8 * Size; if (LogDataCost != 0) { - Instance->chargeGas(LogDataCost); + if (!Instance->chargeGas(LogDataCost)) { + return; + } } uint8_t *MemoryBase = Instance->getMemoryBase(); Data = MemoryBase + Offset; @@ -822,7 +840,10 @@ const uint8_t *evmHandleCreateInternal(zen::runtime::EVMInstance *Instance, evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_SHANGHAI && Size > zen::evm::MAX_SIZE_OF_INITCODE) { - Instance->chargeGas(Instance->getGas() + 1); + if (!Instance->chargeGas(Instance->getGas() + 1)) { + Instance->setReturnData({}); + return ZeroAddress; + } } uint64_t InitCodeWordCost = 0; if (CallKind == EVMC_CREATE2) { @@ -835,7 +856,10 @@ const uint8_t *evmHandleCreateInternal(zen::runtime::EVMInstance *Instance, uint64_t InitCodeWords = (Size + 31) / 32; uint64_t InitCodeCost = InitCodeWordCost * InitCodeWords; if (InitCodeCost != 0) { - Instance->chargeGas(InitCodeCost); + if (!Instance->chargeGas(InitCodeCost)) { + Instance->setReturnData({}); + return ZeroAddress; + } } } @@ -880,7 +904,10 @@ const uint8_t *evmHandleCreateInternal(zen::runtime::EVMInstance *Instance, Result.gas_left > 0 ? static_cast(Result.gas_left) : 0; uint64_t GasUsed = ProvidedGas > GasLeft ? ProvidedGas - GasLeft : 0; if (GasUsed != 0) { - Instance->chargeGas(GasUsed); + if (!Instance->chargeGas(GasUsed)) { + Instance->setReturnData({}); + return ZeroAddress; + } } // Track subcall refund (may be negative) Instance->addGasRefund(Result.gas_refund); @@ -925,7 +952,10 @@ static uint64_t evmHandleCallInternal( evmc_revision Rev = Instance->getRevision(); if (Rev >= EVMC_BERLIN && Module->Host->access_account(TargetAddr) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_ACCOUNT_ACCESS_COST)) { + Instance->setReturnData({}); + return 0; + } } evmc::address CodeAddr = TargetAddr; @@ -979,7 +1009,10 @@ static uint64_t evmHandleCallInternal( } } - Instance->chargeGas(GasCost); + if (!Instance->chargeGas(GasCost)) { + Instance->setReturnData({}); + return 0; + } } uint64_t GasLeft = Instance->getGas(); @@ -995,7 +1028,10 @@ static uint64_t evmHandleCallInternal( if (HasValueArgs && HasValue) { CallGas += zen::evm::CALL_GAS_STIPEND; - Instance->addGas(zen::evm::CALL_GAS_STIPEND); + if (!Instance->addGas(zen::evm::CALL_GAS_STIPEND)) { + Instance->setReturnData({}); + return 0; + } const auto CallerBalance = Module->Host->get_balance(CurrentMsg->recipient); const intx::uint256 CallerValue = intx::be::load(CallerBalance); @@ -1052,7 +1088,10 @@ static uint64_t evmHandleCallInternal( GasLeft = Result.gas_left > 0 ? static_cast(Result.gas_left) : 0; uint64_t GasUsed = CallGas > GasLeft ? CallGas - GasLeft : 0; if (GasUsed > 0) { - Instance->chargeGas(GasUsed); + if (!Instance->chargeGas(GasUsed)) { + Instance->setReturnData({}); + return 0; + } } // Track subcall refund (may be negative) @@ -1171,7 +1210,9 @@ void evmSetCodeCopy(zen::runtime::EVMInstance *Instance, uint64_t DestOffset, return; } if (uint64_t CopyGas = calculateWordCopyGas(Size)) { - Instance->chargeGas(CopyGas); + if (!Instance->chargeGas(CopyGas)) { + return; + } } const zen::runtime::EVMModule *Module = Instance->getModule(); @@ -1203,7 +1244,9 @@ const uint8_t *evmGetKeccak256(zen::runtime::EVMInstance *Instance, } const uint64_t ExtraGas = static_cast(numWords(static_cast(Length))) * 6; - Instance->chargeGas(ExtraGas); + if (!Instance->chargeGas(ExtraGas)) { + return nullptr; + } uint8_t *MemoryBase = Instance->getMemoryBase(); InputData = MemoryBase + Offset; } @@ -1281,11 +1324,19 @@ const intx::uint256 *evmGetSLoad(zen::runtime::EVMInstance *Instance, const auto Key = intx::be::store(Index); if (Rev >= EVMC_BERLIN && Module->Host->access_storage(Msg->recipient, Key) == EVMC_ACCESS_COLD) { - Instance->chargeGas(zen::evm::ADDITIONAL_COLD_SLOAD_COST); + if (!Instance->chargeGas(zen::evm::ADDITIONAL_COLD_SLOAD_COST)) { + return storeUint256Result(intx::uint256{0}); + } } const auto Value = Module->Host->get_storage(Msg->recipient, Key); return storeUint256Result(intx::be::load(Value)); } + +uint64_t evmGetErrorCode(zen::runtime::EVMInstance *Instance) { + return static_cast( + zen::common::to_underlying(Instance->getError().getCode())); +} + void evmSetSStore(zen::runtime::EVMInstance *Instance, const intx::uint256 &Index, const intx::uint256 &Value) { const zen::runtime::EVMModule *Module = Instance->getModule(); @@ -1315,7 +1366,9 @@ void evmSetSStore(zen::runtime::EVMInstance *Instance, const auto [GasCostWarm, GasReFund] = zen::evm::SSTORE_COSTS[Rev][Status]; const auto GasCost = GasCostCold + GasCostWarm; - Instance->chargeGas(GasCost); + if (!Instance->chargeGas(GasCost)) { + return; + } Instance->addGasRefund(GasReFund); } @@ -1363,7 +1416,9 @@ void evmHandleSelfDestruct(zen::runtime::EVMInstance *Instance, const bool IsCold = Module->Host->access_account(BenefAddr) == EVMC_ACCESS_COLD; if (IsCold) { - Instance->chargeGas(zen::evm::COLD_ACCOUNT_ACCESS_COST); + if (!Instance->chargeGas(zen::evm::COLD_ACCOUNT_ACCESS_COST)) { + return; + } } } @@ -1372,7 +1427,9 @@ void evmHandleSelfDestruct(zen::runtime::EVMInstance *Instance, if (Rev == EVMC_TANGERINE_WHISTLE || Module->Host->get_balance(Msg->recipient)) { if (!Module->Host->account_exists(BenefAddr)) { - Instance->chargeGas(zen::evm::ACCOUNT_CREATION_COST); + if (!Instance->chargeGas(zen::evm::ACCOUNT_CREATION_COST)) { + return; + } } } } diff --git a/src/compiler/evm_frontend/evm_imported.h b/src/compiler/evm_frontend/evm_imported.h index a5a90a533..22c67edcb 100644 --- a/src/compiler/evm_frontend/evm_imported.h +++ b/src/compiler/evm_frontend/evm_imported.h @@ -42,6 +42,7 @@ using Bytes32WithUInt64UInt64Fn = using VoidFn = void (*)(zen::runtime::EVMInstance *); using U256WithU256Fn = const intx::uint256 *(*)(zen::runtime::EVMInstance *, const intx::uint256 &); +using ErrorCodeFn = uint64_t (*)(zen::runtime::EVMInstance *); using VoidWithU256U256Fn = void (*)(zen::runtime::EVMInstance *, const intx::uint256 &, const intx::uint256 &); @@ -111,6 +112,7 @@ struct RuntimeFunctions { Bytes32WithUint64Fn GetBlobHash; U256Fn GetBlobBaseFee; U256WithU256Fn GetSLoad; + ErrorCodeFn GetErrorCode; VoidWithU256U256Fn SetSStore; SizeFn GetGas; U256WithU256Fn GetTLoad; @@ -259,6 +261,7 @@ const uint8_t *evmGetKeccak256(zen::runtime::EVMInstance *Instance, void evmHandleFallback(zen::runtime::EVMInstance *Instance, uint64_t PC); const intx::uint256 *evmGetSLoad(zen::runtime::EVMInstance *Instance, const intx::uint256 &Index); +uint64_t evmGetErrorCode(zen::runtime::EVMInstance *Instance); void evmSetSStore(zen::runtime::EVMInstance *Instance, const intx::uint256 &Index, const intx::uint256 &Value); uint64_t evmGetGas(zen::runtime::EVMInstance *Instance); diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index c66401d16..2fdca8295 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -3922,8 +3922,9 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleBalance(Operand Address) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.GetBalance, Address); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.GetBalance, Address); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -3989,7 +3990,7 @@ void EVMMirBuilder::handleCodeCopy(Operand DestOffsetComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetCodeCopy, DestOffsetComponents, OffsetComponents, SizeComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER @@ -4004,7 +4005,7 @@ EVMMirBuilder::handleExtCodeSize(Operand Address) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - auto Result = callRuntimeFor( + auto Result = callRuntimeForWithErrorCheck( RuntimeFunctions.GetExtCodeSize, Address); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); @@ -4018,8 +4019,9 @@ EVMMirBuilder::handleExtCodeHash(Operand Address) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.GetExtCodeHash, Address); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.GetExtCodeHash, Address); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -4554,22 +4556,24 @@ void EVMMirBuilder::handleLogWithTopics(Operand OffsetOp, Operand SizeOp, syncGasToMemory(); #endif if constexpr (NumTopics == 0) { - callRuntimeFor(RuntimeFunctions.EmitLog0, - OffsetOp, SizeOp); + callRuntimeForWithErrorCheck( + RuntimeFunctions.EmitLog0, OffsetOp, SizeOp); } else if constexpr (NumTopics == 1) { - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.EmitLog1, OffsetOp, SizeOp, Topics...); } else if constexpr (NumTopics == 2) { - callRuntimeFor( - RuntimeFunctions.EmitLog2, OffsetOp, SizeOp, Topics...); + callRuntimeForWithErrorCheck(RuntimeFunctions.EmitLog2, + OffsetOp, SizeOp, Topics...); } else if constexpr (NumTopics == 3) { - callRuntimeFor(RuntimeFunctions.EmitLog3, OffsetOp, SizeOp, - Topics...); + callRuntimeForWithErrorCheck( + RuntimeFunctions.EmitLog3, OffsetOp, SizeOp, Topics...); } else { // NumTopics == 4 - callRuntimeFor( - RuntimeFunctions.EmitLog4, OffsetOp, SizeOp, Topics...); + callRuntimeForWithErrorCheck(RuntimeFunctions.EmitLog4, + OffsetOp, SizeOp, Topics...); } #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); @@ -4584,9 +4588,10 @@ EVMMirBuilder::handleCreate(Operand ValueOp, Operand OffsetOp, Operand SizeOp) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - auto Result = callRuntimeFor(RuntimeFunctions.HandleCreate, ValueOp, - OffsetOp, SizeOp); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.HandleCreate, ValueOp, OffsetOp, SizeOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -4603,9 +4608,10 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleCreate2(Operand ValueOp, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.HandleCreate2, ValueOp, OffsetOp, SizeOp, SaltOp); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.HandleCreate2, ValueOp, OffsetOp, SizeOp, SaltOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -4629,8 +4635,9 @@ EVMMirBuilder::handleCall(Operand GasOp, Operand ToAddrOp, Operand ValueOp, syncGasToMemoryFull(); #endif auto Result = - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.HandleCall, GasOp, ToAddrOp, ValueOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER @@ -4656,8 +4663,9 @@ EVMMirBuilder::handleCallCode(Operand GasOp, Operand ToAddrOp, Operand ValueOp, syncGasToMemoryFull(); #endif auto Result = - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.HandleCallCode, GasOp, ToAddrOp, ValueOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER @@ -4676,7 +4684,7 @@ void EVMMirBuilder::handleReturn(Operand MemOffsetComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetReturn, MemOffsetComponents, LengthComponents); // The runtime SetReturn may charge memory expansion gas via chargeGas(), @@ -4708,10 +4716,11 @@ EVMMirBuilder::handleDelegateCall(Operand GasOp, Operand ToAddrOp, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.HandleDelegateCall, GasOp, ToAddrOp, ArgsOffsetOp, - ArgsSizeOp, RetOffsetOp, RetSizeOp); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.HandleDelegateCall, GasOp, ToAddrOp, ArgsOffsetOp, + ArgsSizeOp, RetOffsetOp, RetSizeOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -4734,10 +4743,11 @@ EVMMirBuilder::handleStaticCall(Operand GasOp, Operand ToAddrOp, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.HandleStaticCall, GasOp, ToAddrOp, ArgsOffsetOp, - ArgsSizeOp, RetOffsetOp, RetSizeOp); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.HandleStaticCall, GasOp, ToAddrOp, ArgsOffsetOp, + ArgsSizeOp, RetOffsetOp, RetSizeOp); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -4753,8 +4763,8 @@ void EVMMirBuilder::handleRevert(Operand OffsetOp, Operand SizeOp) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemoryFull(); #endif - callRuntimeFor(RuntimeFunctions.SetRevert, OffsetOp, - SizeOp); + callRuntimeForWithErrorCheck( + RuntimeFunctions.SetRevert, OffsetOp, SizeOp); // The runtime SetRevert may charge memory expansion gas via chargeGas(), // which updates Instance->Gas directly. We must NOT branch to the shared @@ -4809,7 +4819,8 @@ EVMMirBuilder::handleSLoad(Operand KeyComponents) { #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - auto Result = callRuntimeFor( + auto Result = callRuntimeForWithErrorCheck( RuntimeFunctions.GetSLoad, KeyComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); @@ -4822,7 +4833,8 @@ void EVMMirBuilder::handleSStore(Operand KeyComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetSStore, KeyComponents, ValueComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); @@ -4835,7 +4847,8 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleTLoad(Operand Index) { } void EVMMirBuilder::handleTStore(Operand Index, Operand ValueComponents) { const auto &RuntimeFunctions = getRuntimeFunctionTable(); - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetTStore, Index, ValueComponents); } void EVMMirBuilder::handleSelfDestruct(Operand Beneficiary) { @@ -4846,8 +4859,8 @@ void EVMMirBuilder::handleSelfDestruct(Operand Beneficiary) { // leftover amount to the caller. syncGasToMemoryFull(); #endif - callRuntimeFor(RuntimeFunctions.HandleSelfDestruct, - Beneficiary); + callRuntimeForWithErrorCheck( + RuntimeFunctions.HandleSelfDestruct, Beneficiary); // The runtime function (evmHandleSelfDestruct) calls popMessage() which may // set CurrentMessage to nullptr when there is no parent frame. The shared @@ -4872,8 +4885,9 @@ EVMMirBuilder::handleKeccak256(Operand OffsetComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - auto Result = callRuntimeFor( - RuntimeFunctions.GetKeccak256, OffsetComponents, LengthComponents); + auto Result = + callRuntimeForWithErrorCheck( + RuntimeFunctions.GetKeccak256, OffsetComponents, LengthComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER reloadGasFromMemory(); #endif @@ -5653,6 +5667,23 @@ EVMMirBuilder::callRuntimeFor(RetType (*RuntimeFunc)(runtime::EVMInstance *)) { return convertCallResult(CallInstr); } +template +typename EVMMirBuilder::Operand EVMMirBuilder::callRuntimeForWithErrorCheck( + RetType (*RuntimeFunc)(runtime::EVMInstance *)) { + MType *I64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + uint64_t FuncAddr = getFunctionAddress(RuntimeFunc); + MInstruction *FuncAddrInst = createIntConstInstruction(I64Type, FuncAddr); + MInstruction *InstancePtr = getCurrentInstancePointer(); + + MType *ReturnType = getMIRReturnType(); + const bool IsStmt = std::is_same_v; + MInstruction *CallInstr = createInstruction( + IsStmt, ReturnType, FuncAddrInst, + llvm::ArrayRef(InstancePtr)); + emitRuntimeSoftErrorCheck(InstancePtr); + return convertCallResult(CallInstr); +} + // Template helper function to handle uintN_t type conversion (N*64 bits) // example: Support multiple sources for U256 argument: // - BYTES32 pointer -> load 32 bytes and split into 4xI64 @@ -5876,6 +5907,97 @@ EVMMirBuilder::Operand EVMMirBuilder::callRuntimeFor( return convertCallResult(CallInstr); } +template +EVMMirBuilder::Operand EVMMirBuilder::callRuntimeForWithErrorCheck( + RetType (*RuntimeFunc)(runtime::EVMInstance *, ArgTypes...), + const ParamTypes &...Params) { + MType *I64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + uint64_t FuncAddr = getFunctionAddress(RuntimeFunc); + MInstruction *FuncAddrInst = createIntConstInstruction(I64Type, FuncAddr); + MInstruction *InstancePtr = getCurrentInstancePointer(); + + std::vector Args = {InstancePtr}; + auto ParamsTuple = std::forward_as_tuple(Params...); + std::size_t ScratchCursor = 0; + auto PushOne = [this, &Args, &ParamsTuple, &ScratchCursor](auto IndexTag) { + constexpr std::size_t I = decltype(IndexTag)::value; + using ArgT = typename std::tuple_element>::type; + this->appendRuntimeArg(Args, std::get(ParamsTuple), ScratchCursor); + }; + auto PushAll = [&](auto Self, auto IndexTag) -> void { + constexpr std::size_t I = decltype(IndexTag)::value; + if constexpr (I < sizeof...(ArgTypes)) { + PushOne(IndexTag); + Self(Self, std::integral_constant{}); + } + }; + PushAll(PushAll, std::integral_constant{}); + + MType *ReturnType = getMIRReturnType(); + const bool IsStmt = std::is_same_v; + MInstruction *CallInstr = createInstruction( + IsStmt, ReturnType, FuncAddrInst, llvm::ArrayRef{Args}); + emitRuntimeSoftErrorCheck(InstancePtr); + return convertCallResult(CallInstr); +} + +void EVMMirBuilder::emitRuntimeSoftErrorCheck(MInstruction *InstancePtr) { +#if !defined(ZEN_ENABLE_CPU_EXCEPTION) + MType *U64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + MInstruction *GetErrAddr = createIntConstInstruction( + &Ctx.I64Type, getFunctionAddress(evmGetErrorCode)); + MInstruction *ErrCodeInstr = createInstruction( + false, U64Type, GetErrAddr, llvm::ArrayRef(InstancePtr)); + Variable *ErrCodeVar = + storeInstructionInTemp(ErrCodeInstr, ErrCodeInstr->getType()); + MInstruction *ErrCodeValue = loadVariable(ErrCodeVar); + MInstruction *NoErrorCode = createIntConstInstruction( + U64Type, common::to_underlying(ErrorCode::NoError)); + MInstruction *HasNoError = createInstruction( + false, CmpInstruction::Predicate::ICMP_EQ, U64Type, ErrCodeValue, + NoErrorCode); + MBasicBlock *ContinueBB = createBasicBlock(); + MBasicBlock *CheckKnownSoftErrBB = createBasicBlock(); + createInstruction(true, Ctx, HasNoError, ContinueBB, + CheckKnownSoftErrBB); + addSuccessor(ContinueBB); + addSuccessor(CheckKnownSoftErrBB); + setInsertBlock(CheckKnownSoftErrBB); + + // We intentionally do NOT trap on every non-zero code: + // InstanceExit and some control-flow codes are expected runtime states. + // Only trap the two hostapi soft-failure codes that must stop JIT execution. + MInstruction *StaticViolationCode = createIntConstInstruction( + U64Type, common::to_underlying(ErrorCode::EVMStaticModeViolation)); + MInstruction *GasExceededCode = createIntConstInstruction( + U64Type, common::to_underlying(ErrorCode::GasLimitExceeded)); + MInstruction *HasGasExceeded = createInstruction( + false, CmpInstruction::Predicate::ICMP_EQ, U64Type, ErrCodeValue, + GasExceededCode); + MBasicBlock *CheckStaticBB = createBasicBlock(); + MBasicBlock *GasTrapBB = + getOrCreateExceptionSetBB(ErrorCode::GasLimitExceeded); + createInstruction(true, Ctx, HasGasExceeded, GasTrapBB, + CheckStaticBB); + addUniqueSuccessor(GasTrapBB); + addSuccessor(CheckStaticBB); + setInsertBlock(CheckStaticBB); + MInstruction *HasStaticViolation = createInstruction( + false, CmpInstruction::Predicate::ICMP_EQ, + EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64), ErrCodeValue, + StaticViolationCode); + MBasicBlock *StaticTrapBB = + getOrCreateExceptionSetBB(ErrorCode::EVMStaticModeViolation); + createInstruction(true, Ctx, HasStaticViolation, + StaticTrapBB, ContinueBB); + addUniqueSuccessor(StaticTrapBB); + addUniqueSuccessor(ContinueBB); + setInsertBlock(ContinueBB); +#else + (void)InstancePtr; +#endif +} + MInstruction *EVMMirBuilder::getCurrentInstancePointer() { ZEN_ASSERT(InstanceAddr); // Convert instance address back to pointer type @@ -5894,7 +6016,7 @@ void EVMMirBuilder::handleCallDataCopy(Operand DestOffsetComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetCallDataCopy, DestOffsetComponents, OffsetComponents, SizeComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER @@ -5918,7 +6040,8 @@ void EVMMirBuilder::handleExtCodeCopy(Operand AddressComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - callRuntimeFor( + callRuntimeForWithErrorCheck( RuntimeFunctions.SetExtCodeCopy, AddressComponents, DestOffsetComponents, OffsetComponents, SizeComponents); #ifdef ZEN_ENABLE_EVM_GAS_REGISTER @@ -5941,9 +6064,10 @@ void EVMMirBuilder::handleReturnDataCopy(Operand DestOffsetComponents, #ifdef ZEN_ENABLE_EVM_GAS_REGISTER syncGasToMemory(); #endif - Operand StatusOp = callRuntimeFor( - RuntimeFunctions.SetReturnDataCopy, DestOffsetComponents, - OffsetComponents, SizeComponents); + Operand StatusOp = + callRuntimeForWithErrorCheck( + RuntimeFunctions.SetReturnDataCopy, DestOffsetComponents, + OffsetComponents, SizeComponents); MType *I64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); U256Inst StatusParts = extractU256Operand(StatusOp); diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index 6519acc38..8423c334b 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -1035,6 +1035,10 @@ class EVMMirBuilder final { // Template versions of runtime calls template Operand callRuntimeFor(RetType (*RuntimeFunc)(runtime::EVMInstance *)); + // Emits host soft-error checks in check mode after runtime call. + template + Operand callRuntimeForWithErrorCheck( + RetType (*RuntimeFunc)(runtime::EVMInstance *)); template U256Inst convertOperandToInstruction(const Operand &Param); @@ -1049,6 +1053,12 @@ class EVMMirBuilder final { Operand callRuntimeFor(RetType (*RuntimeFunc)(runtime::EVMInstance *, ArgTypes...), const ParamTypes &...Params); + // Emits host soft-error checks in check mode after runtime call. + template + Operand callRuntimeForWithErrorCheck( + RetType (*RuntimeFunc)(runtime::EVMInstance *, ArgTypes...), + const ParamTypes &...Params); + void emitRuntimeSoftErrorCheck(MInstruction *InstancePtr); // Helper template functions for runtime call type mapping template MType *getMIRReturnType(); diff --git a/src/runtime/evm_instance.cpp b/src/runtime/evm_instance.cpp index f635f405a..1cf1fccdc 100644 --- a/src/runtime/evm_instance.cpp +++ b/src/runtime/evm_instance.cpp @@ -206,10 +206,12 @@ void EVMInstance::triggerInstanceExceptionOnJIT(EVMInstance *Inst, } #endif // ZEN_ENABLE_JIT -void EVMInstance::expandMemory(uint64_t RequiredSize) { +bool EVMInstance::expandMemory(uint64_t RequiredSize) { auto NewSize = (RequiredSize + 31) / 32 * 32; uint64_t ExpansionCost = calculateMemoryExpansionCost(MemorySize, NewSize); - chargeGas(ExpansionCost); + if (!chargeGas(ExpansionCost)) { + return false; + } if (NewSize > MemorySize) { if (!MemoryBase) { ensureMemoryBuffer(Memory, MemoryBase); @@ -220,6 +222,7 @@ void EVMInstance::expandMemory(uint64_t RequiredSize) { MemorySize = NewSize; } } + return true; } void EVMInstance::expandMemoryNoGas(uint64_t RequiredSize) { @@ -237,24 +240,34 @@ void EVMInstance::expandMemoryNoGas(uint64_t RequiredSize) { } bool EVMInstance::expandMemoryChecked(uint64_t Offset, uint64_t Size) { + auto markOutOfGasForOversizedMemory = [this]() { + // Intentionally force an OOG path so all modes share one behavior: + // - check mode: chargeGas sets GasLimitExceeded (soft error, no throw) + // - CPU exception mode: trigger trap + // - interpreter mode: throw + (void)chargeGas(getGas() + 1); + }; if (Size == 0) { return true; } uint64_t RequiredSize = 0; if (!calcRequiredMemorySize(Offset, Size, RequiredSize)) { - chargeGas(getGas() + 1); + markOutOfGasForOversizedMemory(); return false; } if (RequiredSize > zen::evm::MAX_REQUIRED_MEMORY_SIZE) { - chargeGas(getGas() + 1); + markOutOfGasForOversizedMemory(); return false; } - expandMemory(RequiredSize); - return true; + return expandMemory(RequiredSize); } bool EVMInstance::expandMemoryChecked(uint64_t OffsetA, uint64_t SizeA, uint64_t OffsetB, uint64_t SizeB) { + auto markOutOfGasForOversizedMemory = [this]() { + // Keep oversize/overflow handling consistent with single-range overload. + (void)chargeGas(getGas() + 1); + }; const bool NeedA = SizeA > 0; const bool NeedB = SizeB > 0; if (!NeedA && !NeedB) { @@ -270,41 +283,49 @@ bool EVMInstance::expandMemoryChecked(uint64_t OffsetA, uint64_t SizeA, uint64_t RequiredSizeB = 0; if (!calcRequiredMemorySize(OffsetA, SizeA, RequiredSizeA) || !calcRequiredMemorySize(OffsetB, SizeB, RequiredSizeB)) { - chargeGas(getGas() + 1); + markOutOfGasForOversizedMemory(); return false; } const uint64_t RequiredSize = std::max(RequiredSizeA, RequiredSizeB); if (RequiredSize > zen::evm::MAX_REQUIRED_MEMORY_SIZE) { - chargeGas(getGas() + 1); + markOutOfGasForOversizedMemory(); return false; } - expandMemory(RequiredSize); - return true; + return expandMemory(RequiredSize); } -void EVMInstance::chargeGas(uint64_t GasCost) { +bool EVMInstance::chargeGas(uint64_t GasCost) { evmc_message *Msg = getCurrentMessage(); ZEN_ASSERT(Msg && "Active message required for gas accounting"); uint64_t GasLeft = getGas(); if (GasLeft < GasCost) { #if defined(ZEN_ENABLE_JIT) && defined(ZEN_ENABLE_CPU_EXCEPTION) triggerInstanceExceptionOnJIT(this, common::ErrorCode::GasLimitExceeded); +#elif defined(ZEN_ENABLE_JIT) && !defined(ZEN_ENABLE_CPU_EXCEPTION) + setInstanceExceptionOnJIT(this, common::ErrorCode::GasLimitExceeded); + return false; #else throw common::getError(common::ErrorCode::GasLimitExceeded); #endif + return false; } uint64_t NewGas = GasLeft - GasCost; setGas(NewGas); Msg->gas = static_cast(NewGas); + return true; } -void EVMInstance::addGas(uint64_t GasAmount) { +bool EVMInstance::addGas(uint64_t GasAmount) { evmc_message *Msg = getCurrentMessage(); ZEN_ASSERT(Msg && "Active message required for gas accounting"); uint64_t GasLeft = getGas(); if (GasLeft > UINT64_MAX - GasAmount) { -#if defined(ZEN_ENABLE_JIT) && defined(ZEN_ENABLE_CPU_EXCEPTION) +#if defined(ZEN_ENABLE_JIT) && !defined(ZEN_ENABLE_CPU_EXCEPTION) + setInstanceExceptionOnJIT(this, common::ErrorCode::GasLimitExceeded); + return false; +#elif defined(ZEN_ENABLE_JIT) && defined(ZEN_ENABLE_CPU_EXCEPTION) triggerInstanceExceptionOnJIT(this, common::ErrorCode::GasLimitExceeded); + return false; #else throw common::getError(common::ErrorCode::GasLimitExceeded); #endif @@ -312,6 +333,7 @@ void EVMInstance::addGas(uint64_t GasAmount) { uint64_t NewGas = GasLeft + GasAmount; setGas(NewGas); Msg->gas = static_cast(NewGas); + return true; } } // namespace zen::runtime diff --git a/src/runtime/evm_instance.h b/src/runtime/evm_instance.h index 63801cb04..6199da8fd 100644 --- a/src/runtime/evm_instance.h +++ b/src/runtime/evm_instance.h @@ -62,13 +62,13 @@ class EVMInstance final : public RuntimeObject { static uint64_t calculateMemoryExpansionCost(uint64_t CurrentSize, uint64_t NewSize); void consumeMemoryExpansionGas(uint64_t RequiredSize); - void expandMemory(uint64_t RequiredSize); + bool expandMemory(uint64_t RequiredSize); void expandMemoryNoGas(uint64_t RequiredSize); bool expandMemoryChecked(uint64_t Offset, uint64_t Size); bool expandMemoryChecked(uint64_t OffsetA, uint64_t SizeA, uint64_t OffsetB, uint64_t SizeB); - void chargeGas(uint64_t GasCost); - void addGas(uint64_t GasAmount); + bool chargeGas(uint64_t GasCost); + bool addGas(uint64_t GasAmount); void addGasRefund(int64_t Amount) { GasRefund += Amount; } void setGasRefund(int64_t Amount) { GasRefund = Amount; }