From e849d52aa8235cc3e1b9eba78525aea4c9e36c35 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 19 Nov 2024 17:57:34 +0100 Subject: [PATCH 1/2] Fix insufficient balance panics --- system-contracts/contracts/EvmEmulator.yul | 62 ++++++++++++------- .../EvmEmulatorFunctions.template.yul | 31 ++++++---- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f9a9c0d75..b96e0e155 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -296,6 +296,12 @@ object "EvmEmulator" { } } + function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } + } + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -817,12 +823,18 @@ object "EvmEmulator" { } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass } } } @@ -1053,12 +1065,7 @@ object "EvmEmulator" { _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow @@ -3344,6 +3351,12 @@ object "EvmEmulator" { } } + function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } + } + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -3865,12 +3878,18 @@ object "EvmEmulator" { } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass } } } @@ -4101,12 +4120,7 @@ object "EvmEmulator" { _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 58736a58d..29c0d25d0 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -234,6 +234,12 @@ function checkOverflow(data1, data2) { } } +function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } +} + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -755,12 +761,18 @@ function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, r } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass } } } @@ -991,12 +1003,7 @@ function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow From ef8b2be0a255011623aab17477c3365a6cc7e827 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 2 Dec 2024 15:19:07 +0100 Subject: [PATCH 2/2] Erase returndata after failed call --- system-contracts/contracts/EvmEmulator.yul | 2 ++ system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul | 1 + 2 files changed, 3 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index b96e0e155..27913afc0 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -835,6 +835,7 @@ object "EvmEmulator" { } default { frameGasLeft := gasToPass + _eraseReturndataPointer() } } } @@ -3890,6 +3891,7 @@ object "EvmEmulator" { } default { frameGasLeft := gasToPass + _eraseReturndataPointer() } } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 29c0d25d0..68c96ef2a 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -773,6 +773,7 @@ function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, r } default { frameGasLeft := gasToPass + _eraseReturndataPointer() } } }