Skip to content

Commit

Permalink
Add boundary checks for ABI encoding values of type UInt & Int
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Sep 20, 2024
1 parent 56ac820 commit 0d1d069
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 45 deletions.
22 changes: 21 additions & 1 deletion fvm/evm/impl/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/errors"
"github.com/onflow/cadence/runtime/interpreter"
"github.com/onflow/cadence/runtime/sema"
gethABI "github.com/onflow/go-ethereum/accounts/abi"
gethCommon "github.com/onflow/go-ethereum/common"

Expand All @@ -21,7 +22,8 @@ const abiEncodingByteSize = 32

// abiEncodingError
type abiEncodingError struct {
Type interpreter.StaticType
Type interpreter.StaticType
Message string
}

var _ errors.UserError = abiEncodingError{}
Expand All @@ -38,6 +40,12 @@ func (e abiEncodingError) Error() string {
b.WriteString(ty.String())
}

message := e.Message
if message != "" {
b.WriteString(": ")
b.WriteString(message)
}

return b.String()
}

Expand Down Expand Up @@ -430,6 +438,12 @@ func encodeABI(

case interpreter.UIntValue:
if staticType == interpreter.PrimitiveStaticTypeUInt {
if value.BigInt.Cmp(sema.UInt256TypeMaxIntBig) > 0 || value.BigInt.Cmp(sema.UInt256TypeMinIntBig) < 0 {
return nil, gethABI.Type{}, abiEncodingError{
Type: value.StaticType(inter),
Message: "value outside the boundaries of uint256",
}
}
return value.BigInt, gethTypeUint, nil
}

Expand Down Expand Up @@ -465,6 +479,12 @@ func encodeABI(

case interpreter.IntValue:
if staticType == interpreter.PrimitiveStaticTypeInt {
if value.BigInt.Cmp(sema.Int256TypeMaxIntBig) > 0 || value.BigInt.Cmp(sema.Int256TypeMinIntBig) < 0 {
return nil, gethABI.Type{}, abiEncodingError{
Type: value.StaticType(inter),
Message: "value outside the boundaries of int256",
}
}
return value.BigInt, gethTypeInt, nil
}

Expand Down
243 changes: 199 additions & 44 deletions fvm/evm/stdlib/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -994,38 +994,11 @@ func TestEVMEncodeDecodeABIRoundtripForUintIntTypes(t *testing.T) {
t.Parallel()

handler := &testContractHandler{}

contractsAddress := flow.BytesToAddress([]byte{0x1})

transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress)
scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress)

rt := runtime.NewInterpreterRuntime(runtime.Config{})

script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
// Check UInt*/Int* encode/decode
let amount: UInt256 = 18446744073709551615
let minBalance: Int256 = -18446744073709551615
let data = EVM.encodeABIWithSignature(
"withdraw(uint,int)",
[UInt(amount), Int(minBalance)]
)
let values = EVM.decodeABIWithSignature(
"withdraw(uint,int)",
types: [Type<UInt>(), Type<Int>()],
data: data
)
assert((values[0] as! UInt) == UInt(amount))
assert((values[1] as! Int) == Int(minBalance))
return true
}
`)

accountCodes := map[common.Location][]byte{}
var events []cadence.Event

Expand Down Expand Up @@ -1073,25 +1046,207 @@ func TestEVMEncodeDecodeABIRoundtripForUintIntTypes(t *testing.T) {
nextTransactionLocation,
)

// Run script
t.Run("with values between the boundaries", func(t *testing.T) {

result, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.NoError(t, err)
script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
// Check UInt/Int encode/decode
let amount: UInt256 = 18446744073709551615
let minBalance: Int256 = -18446744073709551615
let data = EVM.encodeABIWithSignature(
"withdraw(uint,int)",
[UInt(amount), Int(minBalance)]
)
let values = EVM.decodeABIWithSignature(
"withdraw(uint,int)",
types: [Type<UInt>(), Type<Int>()],
data: data
)
assert((values[0] as! UInt) == UInt(amount))
assert((values[1] as! Int) == Int(minBalance))
assert.Equal(t,
cadence.Bool(true),
result,
)
return true
}
`)

// Run script

result, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.NoError(t, err)

assert.Equal(t, cadence.Bool(true), result)
})

t.Run("with values at the boundaries", func(t *testing.T) {

script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
// Check UInt*/Int* encode/decode
let data = EVM.encodeABIWithSignature(
"withdraw(uint,int,uint,int)",
[UInt(UInt256.max), Int(Int256.max),UInt(UInt256.min), Int(Int256.min)]
)
let values = EVM.decodeABIWithSignature(
"withdraw(uint,int,uint,int)",
types: [Type<UInt>(), Type<Int>(),Type<UInt>(), Type<Int>()],
data: data
)
assert((values[0] as! UInt) == UInt(UInt256.max))
assert((values[1] as! Int) == Int(Int256.max))
assert((values[2] as! UInt) == UInt(UInt256.min))
assert((values[3] as! Int) == Int(Int256.min))
return true
}
`)

// Run script

result, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.NoError(t, err)

assert.Equal(t, cadence.Bool(true), result)
})

t.Run("with UInt values outside the boundaries", func(t *testing.T) {

script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
let data = EVM.encodeABIWithSignature(
"withdraw(uint)",
[UInt(UInt256.max)+10]
)
return true
}
`)

// Run script

_, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.Error(t, err)

assert.ErrorContains(
t,
err,
"failed to ABI encode value of type UInt: value outside the boundaries of uint256",
)
})

t.Run("with Int values outside the max boundary", func(t *testing.T) {

script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
let data = EVM.encodeABIWithSignature(
"withdraw(int)",
[Int(Int256.max)+10]
)
return true
}
`)

// Run script

_, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.Error(t, err)

assert.ErrorContains(
t,
err,
"failed to ABI encode value of type Int: value outside the boundaries of int256",
)
})

t.Run("with Int values outside the min boundary", func(t *testing.T) {

script := []byte(`
import EVM from 0x1
access(all)
fun main(): Bool {
let data = EVM.encodeABIWithSignature(
"withdraw(int)",
[Int(Int256.min)-10]
)
return true
}
`)

// Run script

_, err := rt.ExecuteScript(
runtime.Script{
Source: script,
Arguments: [][]byte{},
},
runtime.Context{
Interface: runtimeInterface,
Environment: scriptEnvironment,
Location: nextScriptLocation(),
},
)
require.Error(t, err)

assert.ErrorContains(
t,
err,
"failed to ABI encode value of type Int: value outside the boundaries of int256",
)
})
}

func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) {
Expand Down

0 comments on commit 0d1d069

Please sign in to comment.