Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions executor/executorInstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Instance interface {
MemGrow(pages uint32) error
MemDump() []byte
IsFunctionImported(name string) bool
IsOpcodeUsed(opcode OpcodeUsed) bool
IsInterfaceNil() bool
Reset() bool
SetVMHooksPtr(vmHooksPtr uintptr)
Expand Down
8 changes: 8 additions & 0 deletions executor/executorUsedOpcode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package executor

type OpcodeUsed uint32

const (
OpcodeUsedMemoryCopy OpcodeUsed = iota
OpcodeUsedMemoryFill
)
6 changes: 6 additions & 0 deletions executor/wrapper/wrapperInstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ func (inst *WrapperInstance) IsFunctionImported(name string) bool {
return result
}

// IsOpcodeUsed wraps the call to the underlying instance.
func (inst *WrapperInstance) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
result := inst.wrappedInstance.IsOpcodeUsed(opcode)
return result
}

// IsInterfaceNil returns true if there is no value under the interface.
func (inst *WrapperInstance) IsInterfaceNil() bool {
return inst == nil
Expand Down
11 changes: 11 additions & 0 deletions integrationTests/json/scenariosFeatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ func TestRustPayableFeatures(t *testing.T) {
CheckNoError()
}

func TestRustPanicMessageFeatures(t *testing.T) {
if testing.Short() {
t.Skip("not a short test")
}

ScenariosTest(t).
Folder("features/panic-message-features/scenarios").
Run().
CheckNoError()
}

func TestRustComposability(t *testing.T) {
ScenariosTest(t).
Folder("features/composability/scenarios").
Expand Down
7 changes: 7 additions & 0 deletions mock/context/instanceMock.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type InstanceMock struct {
Exports map[string]string
DefaultErrors map[string]error
Methods map[string]mockMethod
OpcodeUsed map[executor.OpcodeUsed]bool
Points uint64
Data executor.VMHooks
GasLimit uint64
Expand Down Expand Up @@ -183,6 +184,12 @@ func (instance *InstanceMock) IsFunctionImported(name string) bool {
return ok
}

// IsOpcodeUsed mocked method
func (instance *InstanceMock) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
used, ok := instance.OpcodeUsed[opcode]
return ok && used
}

// GetMockInstance gets the mock instance from the runtime of the provided host
func GetMockInstance(host vmhost.VMHost) *InstanceMock {
instance := host.Runtime().GetInstance().(*InstanceMock)
Expand Down
5 changes: 5 additions & 0 deletions mock/context/runtimeContextMock.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ func (r *RuntimeContextMock) IsFunctionImported(_ string) bool {
return true
}

// IsOpcodeUsed mocked method
func (r *RuntimeContextMock) IsOpcodeUsed(_ executor.OpcodeUsed) bool {
return false
}

// AddError mocked method
func (r *RuntimeContextMock) AddError(_ error, _ ...string) {
}
Expand Down
11 changes: 11 additions & 0 deletions mock/context/runtimeContextWrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type RuntimeContextWrapper struct {
// function that will be called by the corresponding RuntimeContext function implementation (by default this will call the same wrapped context function)
IsFunctionImportedFunc func(name string) bool
// function that will be called by the corresponding RuntimeContext function implementation (by default this will call the same wrapped context function)
IsOpcodeUsedFunc func(opcode executor.OpcodeUsed) bool
// function that will be called by the corresponding RuntimeContext function implementation (by default this will call the same wrapped context function)
ReadOnlyFunc func() bool
// function that will be called by the corresponding RuntimeContext function implementation (by default this will call the same wrapped context function)
SetReadOnlyFunc func(readOnly bool)
Expand Down Expand Up @@ -216,6 +218,10 @@ func NewRuntimeContextWrapper(inputRuntimeContext *vmhost.RuntimeContext) *Runti
return runtimeWrapper.runtimeContext.IsFunctionImported(name)
}

runtimeWrapper.IsOpcodeUsedFunc = func(opcode executor.OpcodeUsed) bool {
return runtimeWrapper.runtimeContext.IsOpcodeUsed(opcode)
}

runtimeWrapper.ReadOnlyFunc = func() bool {
return runtimeWrapper.runtimeContext.ReadOnly()
}
Expand Down Expand Up @@ -419,6 +425,11 @@ func (contextWrapper *RuntimeContextWrapper) IsFunctionImported(name string) boo
return contextWrapper.IsFunctionImportedFunc(name)
}

// IsOpcodeUsed calls corresponding xxxFunc function, that by default in turn calls the original method of the wrapped RuntimeContext
func (contextWrapper *RuntimeContextWrapper) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
return contextWrapper.IsOpcodeUsedFunc(opcode)
}

// ReadOnly calls corresponding xxxFunc function, that by default in turn calls the original method of the wrapped RuntimeContext
func (contextWrapper *RuntimeContextWrapper) ReadOnly() bool {
return contextWrapper.ReadOnlyFunc()
Expand Down
18 changes: 18 additions & 0 deletions mock/mockery/Instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,24 @@ func (_m *MockInstance) IsFunctionImported(name string) bool {
return r0
}

// IsOpcodeUsed provides a mock function with given fields: opcode
func (_m *MockInstance) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
ret := _m.Called(opcode)

if len(ret) == 0 {
return false
}

var r0 bool
if rf, ok := ret.Get(0).(func(executor.OpcodeUsed) bool); ok {
r0 = rf(opcode)
} else {
r0 = ret.Get(0).(bool)
}

return r0
}

// IsInterfaceNil provides a mock function with no fields
func (_m *MockInstance) IsInterfaceNil() bool {
ret := _m.Called()
Expand Down
18 changes: 18 additions & 0 deletions mock/mockery/RuntimeContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,24 @@ func (_m *MockRuntimeContext) IsFunctionImported(name string) bool {
return r0
}

// IsOpcodeUsed provides a mock function with given fields: opcode
func (_m *MockRuntimeContext) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
ret := _m.Called(opcode)

if len(ret) == 0 {
return false
}

var r0 bool
if rf, ok := ret.Get(0).(func(executor.OpcodeUsed) bool); ok {
r0 = rf(opcode)
} else {
r0 = ret.Get(0).(bool)
}

return r0
}

// IsReservedFunctionName provides a mock function with given fields: functionName
func (_m *MockRuntimeContext) IsReservedFunctionName(functionName string) bool {
ret := _m.Called(functionName)
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

25 changes: 25 additions & 0 deletions vmhost/contexts/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,14 @@ func (context *runtimeContext) VerifyContractCode() error {
}
}

if !enableEpochsHandler.IsFlagEnabled(vmhost.AsyncV3Flag) {
err = context.checkIfContainsBulkMemoryOpcodes()
if err != nil {
logRuntime.Trace("verify contract code", "error", err)
return err
}
}

logRuntime.Trace("verified contract code")

return nil
Expand All @@ -727,6 +735,18 @@ func (context *runtimeContext) checkIfContainsBarnardOpcodes() error {
return nil
}

func (context *runtimeContext) checkIfContainsBulkMemoryOpcodes() error {
if context.iTracker.Instance().IsOpcodeUsed(executor.OpcodeUsedMemoryCopy) {
return vmhost.ErrContractInvalid
}

if context.iTracker.Instance().IsOpcodeUsed(executor.OpcodeUsedMemoryFill) {
return vmhost.ErrContractInvalid
}

return nil
}

// UseGasBoundedShouldFailExecution returns true when flag activated
func (context *runtimeContext) UseGasBoundedShouldFailExecution() bool {
return context.host.EnableEpochsHandler().IsFlagEnabled(vmhost.UseGasBoundedShouldFailExecutionFlag)
Expand Down Expand Up @@ -831,6 +851,11 @@ func (context *runtimeContext) IsFunctionImported(name string) bool {
return context.iTracker.Instance().IsFunctionImported(name)
}

// IsOpcodeUsed returns true if the WASM module uses the specified opcode.
func (context *runtimeContext) IsOpcodeUsed(opcode executor.OpcodeUsed) bool {
return context.iTracker.Instance().IsOpcodeUsed(opcode)
}

// AddError adds an error to the global error list on runtime context
func (context *runtimeContext) AddError(err error, otherInfo ...string) {
if err == nil {
Expand Down
37 changes: 36 additions & 1 deletion vmhost/hosttest/forbidden_opcodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func TestForbiddenOps_BulkAndSIMD(t *testing.T) {
wasmModules := []string{"data-drop", "memory-init", "memory-fill", "memory-copy", "simd"}
wasmModules := []string{"data-drop", "memory-init", "simd"}

for _, moduleName := range wasmModules {
testCase := testcommon.BuildInstanceCallTest(t).
Expand Down Expand Up @@ -61,3 +61,38 @@ func TestBarnardOpcodesActivation(t *testing.T) {
ContractInvalid()
})
}

func TestBulkMemoryOpcodesActivation(t *testing.T) {
wasmModules := []string{"memory-copy", "memory-fill"}

for _, moduleName := range wasmModules {
testcommon.BuildInstanceCreatorTest(t).
WithInput(testcommon.CreateTestContractCreateInputBuilder().
WithGasProvided(100000000).
WithContractCode(testcommon.GetTestSCCodeModule("forbidden-opcodes/"+moduleName, moduleName, "../../")).
Build()).
WithEnableEpochsHandler(&worldmock.EnableEpochsHandlerStub{
IsFlagEnabledCalled: func(flag core.EnableEpochFlag) bool {
return flag != vmhost.AsyncV3Flag
},
}).
AndAssertResults(func(stubBlockchainHook *contextmock.BlockchainHookStub, verify *testcommon.VMOutputVerifier) {
verify.
ContractInvalid()
})

testcommon.BuildInstanceCreatorTest(t).
WithInput(testcommon.CreateTestContractCreateInputBuilder().
WithGasProvided(100000000).
WithContractCode(testcommon.GetTestSCCodeModule("forbidden-opcodes/"+moduleName, moduleName, "../../")).
Build()).
WithEnableEpochsHandler(&worldmock.EnableEpochsHandlerStub{
IsFlagEnabledCalled: func(flag core.EnableEpochFlag) bool {
return true
},
}).
AndAssertResults(func(stubBlockchainHook *contextmock.BlockchainHookStub, verify *testcommon.VMOutputVerifier) {
verify.FunctionNotFound()
})
}
}
1 change: 1 addition & 0 deletions vmhost/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type RuntimeContext interface {
GetInstanceStackSize() uint64
CountSameContractInstancesOnStack(address []byte) uint64
IsFunctionImported(name string) bool
IsOpcodeUsed(opcode executor.OpcodeUsed) bool
ReadOnly() bool
SetReadOnly(readOnly bool)
StartWasmerInstance(contract []byte, gasLimit uint64, newCode bool) error
Expand Down
2 changes: 2 additions & 0 deletions vmhost/vmhooks/generate/cmd/input/wasmer2_opcodes_short.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ LocalAllocate
Loop
MemoryGrow
MemorySize
MemoryCopy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need on VerifyContract and activation flag and to check these opcodes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added activation flags and checks

MemoryFill
Nop
RefFunc
RefIsNull
Expand Down
7 changes: 7 additions & 0 deletions wasmer2/bridge2.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ func cWasmerInstanceHasImportedFunction(
))
}

func cWasmerInstanceIsOpcodeUsed(instance *cWasmerInstanceT, opcode uint32) cInt {
return (cInt)(C.vm_exec_instance_is_opcode_used(
(*C.vm_exec_instance_t)(instance),
(C.uint32_t)(opcode),
))
}

func cWasmerInstanceExportedFunctionNamesLength(instance *cWasmerInstanceT) cInt {
return (cInt)(C.vm_exported_function_names_length(
(*C.vm_exec_instance_t)(instance),
Expand Down
Binary file modified wasmer2/libvmexeccapi.dylib
Binary file not shown.
Loading
Loading