diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 3ffcf9e48e..2848fc65e5 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -33,6 +33,7 @@ import ( "github.com/klaytn/klaytn/common" "github.com/klaytn/klaytn/common/math" "github.com/klaytn/klaytn/crypto" + "github.com/stretchr/testify/assert" ) const jsondata = ` @@ -763,6 +764,41 @@ func TestUnpackEvent(t *testing.T) { } } +// Testset (ABI and hexdata) was created based on this contract format. +/* +contract T { + event eventInDynamicType(uint elem1, string[3] elem2); + constructor() {} + function test123() public { + string[3] memory vals = ["A...","B...","C..."]; + emit eventInDynamicType(123, vals); + } + } +*/ +func TestUnpackEventOffsetBound(t *testing.T) { + const abiJSON = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"elem1","type":"uint256"},{"indexed":false,"internalType":"string[3]","name":"elem2","type":"string[3]"}],"name":"ev123","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"elem1","type":"uint256"},{"indexed":false,"internalType":"string[3]","name":"elem2","type":"string[3]"}],"name":"eventInDynamicType","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"receivedAddr","type":"event"},{"inputs":[],"name":"test123","outputs":[],"stateMutability":"nonpayable","type":"function"}]` + + abi, err := JSON(strings.NewReader(abiJSON)) + assert.Nil(t, err, err) + + const rawData = `000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000354242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242000000000000000000000000000000000000000000000000000000000000000000000000000000000000684343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343000000000000000000000000000000000000000000000000` + // Modify offset value (0x40 -> 0xffff) to attemp out-of-bounds access + modifiedRawData := rawData[:124] + "ffff" + rawData[128:] + + data, err := hex.DecodeString(modifiedRawData) + assert.Nil(t, err) + + type evObj struct { + Elem1 *big.Int + Elem2 [3]string + } + + var params evObj + err = abi.Unpack(¶ms, "eventInDynamicType", data) + // Must return error + assert.NotNil(t, err, err) +} + func TestUnpackEventIntoMap(t *testing.T) { const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` abi, err := JSON(strings.NewReader(abiJSON)) diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go index 46c4c3ad1d..3e81075159 100644 --- a/accounts/abi/unpack.go +++ b/accounts/abi/unpack.go @@ -227,7 +227,10 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) { return forEachUnpack(t, output[begin:], 0, length) case ArrayTy: if isDynamicType(*t.Elem) { - offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:])) + offset := binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]) + if offset > uint64(len(output)) { + return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output)) + } return forEachUnpack(t, output[offset:], 0, t.Size) } return forEachUnpack(t, output[index:], 0, t.Size)