diff --git a/math/overflow.go b/math/overflow.go index ce6b7635d..043d18fde 100644 --- a/math/overflow.go +++ b/math/overflow.go @@ -90,3 +90,23 @@ func SubInt(a, b int) int { log.Trace("SubInt underflow", "a", a, "b", b) return builtinMath.MinInt64 } + +// MulInt32 performs multiplication on int32 and logs an error if the multiplication overflows +func MulInt32(a, b int32) int32 { + res, err := MulInt32WithErr(a, b) + if err != nil { + log.Trace("MulInt32 overflow", "a", a, "b", b) + return builtinMath.MaxInt32 + } + + return res +} + +// MulInt32WithErr performs multiplication on int32 and returns an error if the multiplication overflows +func MulInt32WithErr(a, b int32) (int32, error) { + wide := int64(a) * int64(b) + if wide > builtinMath.MaxInt32 || wide < builtinMath.MinInt32 { + return builtinMath.MaxInt32, ErrMultiplicationOverflow + } + return int32(wide), nil +} diff --git a/mock/context/runtimeContextMock.go b/mock/context/runtimeContextMock.go index 7f6771cc6..7a02e267c 100644 --- a/mock/context/runtimeContextMock.go +++ b/mock/context/runtimeContextMock.go @@ -320,6 +320,11 @@ func (r *RuntimeContextMock) UseGasBoundedShouldFailExecution() bool { return true } +// AttributeExtraGasUsage mocked method +func (r *RuntimeContextMock) AttributeExtraGasUsage() bool { + return true +} + // FailExecution mocked method func (r *RuntimeContextMock) FailExecution(_ error) { } diff --git a/mock/context/runtimeContextWrapper.go b/mock/context/runtimeContextWrapper.go index 86c1b85cc..d9c0e6b86 100644 --- a/mock/context/runtimeContextWrapper.go +++ b/mock/context/runtimeContextWrapper.go @@ -479,6 +479,11 @@ func (contextWrapper *RuntimeContextWrapper) UseGasBoundedShouldFailExecution() return contextWrapper.runtimeContext.UseGasBoundedShouldFailExecution() } +// AttributeExtraGasUsage calls corresponding xxxFunc function, that by default in turn calls the original method of the wrapped RuntimeContext +func (contextWrapper *RuntimeContextWrapper) AttributeExtraGasUsage() bool { + return contextWrapper.runtimeContext.AttributeExtraGasUsage() +} + // GetVMExecutor calls corresponding xxxFunc function, that by default in turn calls the original method of the wrapped RuntimeContext func (contextWrapper *RuntimeContextWrapper) GetVMExecutor() executor.Executor { return contextWrapper.GetVMExecutorFunc() diff --git a/vmhost/contexts/runtime.go b/vmhost/contexts/runtime.go index 40e9eb1ba..5c13d109e 100644 --- a/vmhost/contexts/runtime.go +++ b/vmhost/contexts/runtime.go @@ -267,7 +267,7 @@ func (context *runtimeContext) makeInstanceFromContractByteCode(contract []byte, context.iTracker.SetCodeHash(codeHash) } - if newCode { + if newCode || context.verifyCode { err = context.VerifyContractCode() if err != nil { context.iTracker.ForceCleanInstance(true) @@ -733,6 +733,11 @@ func (context *runtimeContext) UseGasBoundedShouldFailExecution() bool { return context.host.EnableEpochsHandler().IsFlagEnabled(vmhost.UseGasBoundedShouldFailExecutionFlag) } +// AttributeExtraGasUsage returns true when flag is activated +func (context *runtimeContext) AttributeExtraGasUsage() bool { + return context.host.EnableEpochsHandler().IsFlagEnabled(vmhost.AttributeExtraGasUsageFlag) +} + // GetPointsUsed returns the gas amount spent by the currently running Wasmer instance. func (context *runtimeContext) GetPointsUsed() uint64 { if check.IfNil(context.iTracker.Instance()) { diff --git a/vmhost/flags.go b/vmhost/flags.go index 911c22c82..9ddfd2809 100644 --- a/vmhost/flags.go +++ b/vmhost/flags.go @@ -30,5 +30,8 @@ const ( // FixGetBalanceFlag defines the flag that activates the fix for get balance from the Barnard release FixGetBalanceFlag core.EnableEpochFlag = "FixGetBalanceFlag" + // AttributeExtraGasUsageFlag defines the flag that activates the extra gas usage for extra attributes + AttributeExtraGasUsageFlag core.EnableEpochFlag = "AttributeExtraGasUsageFlag" + // all new flags must be added to allFlags slice from hostCore/host ) diff --git a/vmhost/hostCore/execution.go b/vmhost/hostCore/execution.go index 8135e8fe5..e2697a8d3 100644 --- a/vmhost/hostCore/execution.go +++ b/vmhost/hostCore/execution.go @@ -891,7 +891,7 @@ func (host *vmHost) execute(input *vmcommon.ContractCallInput) error { // Replace the current Wasmer instance of the Runtime with a new one; this // assumes that the instance was preserved on the Runtime instance stack // before calling executeSmartContractCall(). - err = runtime.StartWasmerInstance(contract, metering.GetGasForExecution(), false) + err = runtime.StartWasmerInstance(contract, metering.GetGasForExecution(), input.AllowInitFunction) if err != nil { return err } diff --git a/vmhost/hostCore/host.go b/vmhost/hostCore/host.go index e6c72a04a..47d636a94 100644 --- a/vmhost/hostCore/host.go +++ b/vmhost/hostCore/host.go @@ -46,6 +46,7 @@ var allFlags = []core.EnableEpochFlag{ vmhost.ValidationOnGobDecodeFlag, vmhost.BarnardOpcodesFlag, vmhost.FixGetBalanceFlag, + vmhost.AttributeExtraGasUsageFlag, } // vmHost implements HostContext interface. diff --git a/vmhost/hosttest/execution_test.go b/vmhost/hosttest/execution_test.go index a0121545b..69fee8e17 100644 --- a/vmhost/hosttest/execution_test.go +++ b/vmhost/hosttest/execution_test.go @@ -949,7 +949,7 @@ func TestExecution_ManagedBuffers(t *testing.T) { test.CreateInstanceContract(test.ParentAddress). WithCode(test.GetTestSCCode("managed-buffers", "../../"))). WithInput(test.CreateTestContractCallInputBuilder(). - WithGasProvided(100000). + WithGasProvided(1000000). WithFunction(mBuffer[functionNumber]). // mBufferFromBigIntUnsignedTest WithArguments([]byte{byte(numberOfReps)}). Build()). diff --git a/vmhost/interface.go b/vmhost/interface.go index 1f12afbfa..9c1a4694f 100644 --- a/vmhost/interface.go +++ b/vmhost/interface.go @@ -155,6 +155,7 @@ type RuntimeContext interface { GetPointsUsed() uint64 SetPointsUsed(gasPoints uint64) UseGasBoundedShouldFailExecution() bool + AttributeExtraGasUsage() bool CleanInstance() AddError(err error, otherInfo ...string) diff --git a/vmhost/vmhooks/baseOps.go b/vmhost/vmhooks/baseOps.go index 84ecd24cf..4301310a1 100644 --- a/vmhost/vmhooks/baseOps.go +++ b/vmhost/vmhooks/baseOps.go @@ -1016,9 +1016,10 @@ func (context *VMHooksImpl) MultiTransferESDTNFTExecute( return 1 } + numArgsFromMemory := math.MulInt32(numTokenTransfers, parsers.ArgsPerTransfer) transferArgs, _, err := context.getArgumentsFromMemory( host, - numTokenTransfers*parsers.ArgsPerTransfer, + numArgsFromMemory, tokenTransfersArgsLengthOffset, tokenTransferDataOffset, ) diff --git a/vmhost/vmhooks/bigIntOps.go b/vmhost/vmhooks/bigIntOps.go index 580d8522d..8c1d81a0d 100644 --- a/vmhost/vmhooks/bigIntOps.go +++ b/vmhost/vmhooks/bigIntOps.go @@ -773,6 +773,8 @@ func (context *VMHooksImpl) BigIntEMod(destinationHandle, op1Handle, op2Handle i dest.Mod(a, b) // Mod implements Euclidean division (unlike Go) } +const maxSqrtLen = 8000 + // BigIntSqrt VMHooks implementation. // @autogenerate(VMHooks) func (context *VMHooksImpl) BigIntSqrt(destinationHandle, opHandle int32) { @@ -804,6 +806,12 @@ func (context *VMHooksImpl) BigIntSqrt(destinationHandle, opHandle int32) { context.FailExecution(vmhost.ErrBadLowerBounds) return } + + if context.GetRuntimeContext().AttributeExtraGasUsage() && a.BitLen() > maxSqrtLen { + context.FailExecution(vmhost.ErrBadUpperBounds) + return + } + dest.Sqrt(a) } diff --git a/vmhost/vmhooks/manBufOps.go b/vmhost/vmhooks/manBufOps.go index 89f057054..f8f245f60 100644 --- a/vmhost/vmhooks/manBufOps.go +++ b/vmhost/vmhooks/manBufOps.go @@ -534,6 +534,13 @@ func (context *VMHooksImpl) MBufferFromBigIntUnsigned(mBufferHandle int32, bigIn return 1 } + byteLen := (value.BitLen() + 7) / 8 + err = chargeGasForExtraLength(byteLen, context.GetVMHost()) + if err != nil { + context.FailExecution(err) + return -1 + } + managedType.SetBytes(mBufferHandle, value.Bytes()) return 0 @@ -558,6 +565,13 @@ func (context *VMHooksImpl) MBufferFromBigIntSigned(mBufferHandle int32, bigIntH return 1 } + byteLen := (value.BitLen() + 7) / 8 + err = chargeGasForExtraLength(byteLen, context.GetVMHost()) + if err != nil { + context.FailExecution(err) + return -1 + } + managedType.SetBytes(mBufferHandle, twos.ToBytes(value)) return 0 } diff --git a/vmhost/vmhooks/managedei.go b/vmhost/vmhooks/managedei.go index 29564528e..baa02b542 100644 --- a/vmhost/vmhooks/managedei.go +++ b/vmhost/vmhooks/managedei.go @@ -57,6 +57,7 @@ const ( ) const EGLDTokenName = "EGLD-000000" // TODO: maybe move to core? +const safeAttributeLength = 100 // ManagedSCAddress VMHooks implementation. // @autogenerate(VMHooks) @@ -1528,6 +1529,12 @@ func ManagedIsESDTPausedWithHost(host vmhost.VMHost, tokenIDHandle int32) int32 return -1 } + err = chargeGasForExtraLength(len(tokenID), host) + if err != nil { + FailExecution(host, err) + return -1 + } + if blockchain.IsPaused(tokenID) { return 1 } @@ -1669,6 +1676,13 @@ func ManagedIsBuiltinFunctionWithHost(host vmhost.VMHost, functionNameHandle int return -1 } + lenFuncName := len(mBuffFunctionName) + err = chargeGasForExtraLength(lenFuncName, host) + if err != nil { + FailExecution(host, err) + return -1 + } + isBuiltinFunction := host.IsBuiltinFunctionName(string(mBuffFunctionName)) if isBuiltinFunction { return 1 @@ -1676,3 +1690,13 @@ func ManagedIsBuiltinFunctionWithHost(host vmhost.VMHost, functionNameHandle int return 0 } + +func chargeGasForExtraLength(lenAttribute int, host vmhost.VMHost) error { + if !host.Runtime().AttributeExtraGasUsage() || lenAttribute < safeAttributeLength { + return nil + } + + metering := host.Metering() + gasToUse := math.MulUint64(metering.GasSchedule().BaseOperationCost.DataCopyPerByte, uint64(lenAttribute)) + return metering.UseGasBounded(gasToUse) +} diff --git a/vmhost/vmhookstest/manBuffers_test.go b/vmhost/vmhookstest/manBuffers_test.go index 467d53262..df543c74d 100644 --- a/vmhost/vmhookstest/manBuffers_test.go +++ b/vmhost/vmhookstest/manBuffers_test.go @@ -226,7 +226,7 @@ func TestManBuffers_mBufferFromBigIntUnsigned(t *testing.T) { test.CreateInstanceContract(test.ParentAddress). WithCode(test.GetTestSCCode("managed-buffers", "../../"))). WithInput(test.CreateTestContractCallInputBuilder(). - WithGasProvided(100000). + WithGasProvided(1000000). WithFunction("mBufferFromBigIntUnsignedTest"). WithArguments([]byte{byte(numberOfReps)}). Build()).