Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
f54506cc
Unverified
Commit
f54506cc
authored
Feb 21, 2018
by
Martin Holst Swende
Committed by
GitHub
Feb 21, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15770 from holiman/abi_nostruct
accounts/abi: add another unpack interface
parents
b585f761
61f2279b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
166 additions
and
63 deletions
+166
-63
abi.go
accounts/abi/abi.go
+3
-3
abi_test.go
accounts/abi/abi_test.go
+5
-1
argument.go
accounts/abi/argument.go
+60
-50
unpack.go
accounts/abi/unpack.go
+27
-8
unpack_test.go
accounts/abi/unpack_test.go
+71
-1
No files found.
accounts/abi/abi.go
View file @
f54506cc
...
...
@@ -136,11 +136,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
// MethodById looks up a method by the 4-byte id
// returns nil if none found
func
(
abi
*
ABI
)
MethodById
(
sigdata
[]
byte
)
*
Method
{
func
(
abi
*
ABI
)
MethodById
(
sigdata
[]
byte
)
(
*
Method
,
error
)
{
for
_
,
method
:=
range
abi
.
Methods
{
if
bytes
.
Equal
(
method
.
Id
(),
sigdata
[
:
4
])
{
return
&
method
return
&
method
,
nil
}
}
return
nil
return
nil
,
fmt
.
Errorf
(
"no method with id: %#x"
,
sigdata
[
:
4
])
}
accounts/abi/abi_test.go
View file @
f54506cc
...
...
@@ -689,7 +689,11 @@ func TestABI_MethodById(t *testing.T) {
}
for
name
,
m
:=
range
abi
.
Methods
{
a
:=
fmt
.
Sprintf
(
"%v"
,
m
)
b
:=
fmt
.
Sprintf
(
"%v"
,
abi
.
MethodById
(
m
.
Id
()))
m2
,
err
:=
abi
.
MethodById
(
m
.
Id
())
if
err
!=
nil
{
t
.
Fatalf
(
"Failed to look up ABI method: %v"
,
err
)
}
b
:=
fmt
.
Sprintf
(
"%v"
,
m2
)
if
a
!=
b
{
t
.
Errorf
(
"Method %v (id %v) not 'findable' by id in ABI"
,
name
,
common
.
ToHex
(
m
.
Id
()))
}
...
...
accounts/abi/argument.go
View file @
f54506cc
...
...
@@ -67,6 +67,17 @@ func (arguments Arguments) LengthNonIndexed() int {
return
out
}
// NonIndexed returns the arguments with indexed arguments filtered out
func
(
arguments
Arguments
)
NonIndexed
()
Arguments
{
var
ret
[]
Argument
for
_
,
arg
:=
range
arguments
{
if
!
arg
.
Indexed
{
ret
=
append
(
ret
,
arg
)
}
}
return
ret
}
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func
(
arguments
Arguments
)
isTuple
()
bool
{
return
len
(
arguments
)
>
1
...
...
@@ -74,21 +85,25 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format
func
(
arguments
Arguments
)
Unpack
(
v
interface
{},
data
[]
byte
)
error
{
if
arguments
.
isTuple
()
{
return
arguments
.
unpackTuple
(
v
,
data
)
}
return
arguments
.
unpackAtomic
(
v
,
data
)
}
func
(
arguments
Arguments
)
unpackTuple
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is arguments pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
if
reflect
.
Ptr
!=
reflect
.
ValueOf
(
v
)
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
marshalledValues
,
err
:=
arguments
.
UnpackValues
(
data
)
if
err
!=
nil
{
return
err
}
if
arguments
.
isTuple
()
{
return
arguments
.
unpackTuple
(
v
,
marshalledValues
)
}
return
arguments
.
unpackAtomic
(
v
,
marshalledValues
)
}
func
(
arguments
Arguments
)
unpackTuple
(
v
interface
{},
marshalledValues
[]
interface
{})
error
{
var
(
value
=
valueOf
.
Elem
()
value
=
reflect
.
ValueOf
(
v
)
.
Elem
()
typ
=
value
.
Type
()
kind
=
value
.
Kind
()
)
...
...
@@ -110,30 +125,9 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
exists
[
field
]
=
true
}
}
// `i` counts the nonindexed arguments.
// `j` counts the number of complex types.
// both `i` and `j` are used to to correctly compute `data` offset.
for
i
,
arg
:=
range
arguments
.
NonIndexed
()
{
i
,
j
:=
-
1
,
0
for
_
,
arg
:=
range
arguments
{
if
arg
.
Indexed
{
// can't read, continue
continue
}
i
++
marshalledValue
,
err
:=
toGoType
((
i
+
j
)
*
32
,
arg
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
arg
.
Type
.
T
==
ArrayTy
{
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j
+=
arg
.
Type
.
Size
-
1
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
reflectValue
:=
reflect
.
ValueOf
(
marshalledValues
[
i
])
switch
kind
{
case
reflect
.
Struct
:
...
...
@@ -166,34 +160,52 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func
(
arguments
Arguments
)
unpackAtomic
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is arguments pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
arg
:=
arguments
[
0
]
if
arg
.
Indexed
{
return
fmt
.
Errorf
(
"abi: attempting to unpack indexed variable into element."
)
func
(
arguments
Arguments
)
unpackAtomic
(
v
interface
{},
marshalledValues
[]
interface
{})
error
{
if
len
(
marshalledValues
)
!=
1
{
return
fmt
.
Errorf
(
"abi: wrong length, expected single value, got %d"
,
len
(
marshalledValues
))
}
elem
:=
reflect
.
ValueOf
(
v
)
.
Elem
()
reflectValue
:=
reflect
.
ValueOf
(
marshalledValues
[
0
])
return
set
(
elem
,
reflectValue
,
arguments
.
NonIndexed
()[
0
])
}
value
:=
valueOf
.
Elem
()
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
// without supplying a struct to unpack into. Instead, this method returns a list containing the
// values. An atomic argument will be a list with one element.
func
(
arguments
Arguments
)
UnpackValues
(
data
[]
byte
)
([]
interface
{},
error
)
{
retval
:=
make
([]
interface
{},
0
,
arguments
.
LengthNonIndexed
())
virtualArgs
:=
0
for
index
,
arg
:=
range
arguments
.
NonIndexed
()
{
marshalledValue
,
err
:=
toGoType
((
index
+
virtualArgs
)
*
32
,
arg
.
Type
,
data
)
if
arg
.
Type
.
T
==
ArrayTy
{
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on
marshalledValue
,
err
:=
toGoType
(
0
,
arg
.
Type
,
output
)
if
err
!=
nil
{
return
err
virtualArgs
+=
arg
.
Type
.
Size
-
1
}
if
err
!=
nil
{
return
nil
,
err
}
retval
=
append
(
retval
,
marshalledValue
)
}
return
set
(
value
,
reflect
.
ValueOf
(
marshalledValue
),
arg
)
return
retval
,
nil
}
// Unpack performs the operation Go format -> Hexdata
// PackValues performs the operation Go format -> Hexdata
// It is the semantic opposite of UnpackValues
func
(
arguments
Arguments
)
PackValues
(
args
[]
interface
{})
([]
byte
,
error
)
{
return
arguments
.
Pack
(
args
...
)
}
// Pack performs the operation Go format -> Hexdata
func
(
arguments
Arguments
)
Pack
(
args
...
interface
{})
([]
byte
,
error
)
{
// Make sure arguments match up and pack them
abiArgs
:=
arguments
if
len
(
args
)
!=
len
(
abiArgs
)
{
return
nil
,
fmt
.
Errorf
(
"argument count mismatch: %d for %d"
,
len
(
args
),
len
(
abiArgs
))
}
// variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input.
var
variableInput
[]
byte
...
...
@@ -207,7 +219,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
inputOffset
+=
32
}
}
var
ret
[]
byte
for
i
,
a
:=
range
args
{
input
:=
abiArgs
[
i
]
...
...
@@ -216,7 +227,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
if
err
!=
nil
{
return
nil
,
err
}
// check for a slice type (string, bytes, slice)
if
input
.
Type
.
requiresLengthPrefix
()
{
// calculate the offset
...
...
accounts/abi/unpack.go
View file @
f54506cc
...
...
@@ -95,6 +95,9 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
// iteratively unpack elements
func
forEachUnpack
(
t
Type
,
output
[]
byte
,
start
,
size
int
)
(
interface
{},
error
)
{
if
size
<
0
{
return
nil
,
fmt
.
Errorf
(
"cannot marshal input to array, size is negative (%d)"
,
size
)
}
if
start
+
32
*
size
>
len
(
output
)
{
return
nil
,
fmt
.
Errorf
(
"abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)"
,
len
(
output
),
start
+
32
*
size
)
}
...
...
@@ -181,16 +184,32 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
func
lengthPrefixPointsTo
(
index
int
,
output
[]
byte
)
(
start
int
,
length
int
,
err
error
)
{
offset
:=
int
(
binary
.
BigEndian
.
Uint64
(
output
[
index
+
24
:
index
+
32
]))
if
offset
+
32
>
len
(
output
)
{
return
0
,
0
,
fmt
.
Errorf
(
"abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)"
,
len
(
output
),
offset
+
32
)
bigOffsetEnd
:=
big
.
NewInt
(
0
)
.
SetBytes
(
output
[
index
:
index
+
32
])
bigOffsetEnd
.
Add
(
bigOffsetEnd
,
common
.
Big32
)
outputLength
:=
big
.
NewInt
(
int64
(
len
(
output
)))
if
bigOffsetEnd
.
Cmp
(
outputLength
)
>
0
{
return
0
,
0
,
fmt
.
Errorf
(
"abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)"
,
bigOffsetEnd
,
outputLength
)
}
length
=
int
(
binary
.
BigEndian
.
Uint64
(
output
[
offset
+
24
:
offset
+
32
]))
if
offset
+
32
+
length
>
len
(
output
)
{
return
0
,
0
,
fmt
.
Errorf
(
"abi
: cannot marshal in to go type: length insufficient %d require %d"
,
len
(
output
),
offset
+
32
+
length
)
if
bigOffsetEnd
.
BitLen
()
>
63
{
return
0
,
0
,
fmt
.
Errorf
(
"abi
offset larger than int64: %v"
,
bigOffsetEnd
)
}
start
=
offset
+
32
//fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
offsetEnd
:=
int
(
bigOffsetEnd
.
Uint64
())
lengthBig
:=
big
.
NewInt
(
0
)
.
SetBytes
(
output
[
offsetEnd
-
32
:
offsetEnd
])
totalSize
:=
big
.
NewInt
(
0
)
totalSize
.
Add
(
totalSize
,
bigOffsetEnd
)
totalSize
.
Add
(
totalSize
,
lengthBig
)
if
totalSize
.
BitLen
()
>
63
{
return
0
,
0
,
fmt
.
Errorf
(
"abi length larger than int64: %v"
,
totalSize
)
}
if
totalSize
.
Cmp
(
outputLength
)
>
0
{
return
0
,
0
,
fmt
.
Errorf
(
"abi: cannot marshal in to go type: length insufficient %v require %v"
,
outputLength
,
totalSize
)
}
start
=
int
(
bigOffsetEnd
.
Uint64
())
length
=
int
(
lengthBig
.
Uint64
())
return
}
accounts/abi/unpack_test.go
View file @
f54506cc
...
...
@@ -130,7 +130,7 @@ var unpackTests = []unpackTest{
{
def
:
`[{"type": "bytes32"}]`
,
enc
:
"0100000000000000000000000000000000000000000000000000000000000000"
,
want
:
common
.
HexToHash
(
"0100000000000000000000000000000000000000000000000000000000000000"
)
,
want
:
[
32
]
byte
{
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
}
,
},
{
def
:
`[{"type": "function"}]`
,
...
...
@@ -683,3 +683,73 @@ func TestUnmarshal(t *testing.T) {
t
.
Fatal
(
"expected error:"
,
err
)
}
}
func
TestOOMMaliciousInput
(
t
*
testing
.
T
)
{
oomTests
:=
[]
unpackTest
{
{
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020"
+
// offset
"0000000000000000000000000000000000000000000000000000000000000003"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Length larger than 64 bits
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020"
+
// offset
"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Offset very large (over 64 bits)
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000020"
+
// offset
"0000000000000000000000000000000000000000000000000000000000000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Offset very large (below 64 bits)
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000007ffffffffff00020"
+
// offset
"0000000000000000000000000000000000000000000000000000000000000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Offset negative (as 64 bit)
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"000000000000000000000000000000000000000000000000f000000000000020"
+
// offset
"0000000000000000000000000000000000000000000000000000000000000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Negative length
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020"
+
// offset
"000000000000000000000000000000000000000000000000f000000000000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
{
// Very large length
def
:
`[{"type": "uint8[]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020"
+
// offset
"0000000000000000000000000000000000000000000000007fffffffff000002"
+
// num elems
"0000000000000000000000000000000000000000000000000000000000000001"
+
// elem 1
"0000000000000000000000000000000000000000000000000000000000000002"
,
// elem 2
},
}
for
i
,
test
:=
range
oomTests
{
def
:=
fmt
.
Sprintf
(
`[{ "name" : "method", "outputs": %s}]`
,
test
.
def
)
abi
,
err
:=
JSON
(
strings
.
NewReader
(
def
))
if
err
!=
nil
{
t
.
Fatalf
(
"invalid ABI definition %s: %v"
,
def
,
err
)
}
encb
,
err
:=
hex
.
DecodeString
(
test
.
enc
)
if
err
!=
nil
{
t
.
Fatalf
(
"invalid hex: %s"
+
test
.
enc
)
}
_
,
err
=
abi
.
Methods
[
"method"
]
.
Outputs
.
UnpackValues
(
encb
)
if
err
==
nil
{
t
.
Fatalf
(
"Expected error on malicious input, test %d"
,
i
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment