Skip to content

Commit b5013e1

Browse files
committed
core/vm: Limit JUMPDEST analysis only to code section of EOF
1 parent 0932483 commit b5013e1

File tree

3 files changed

+46
-15
lines changed

3 files changed

+46
-15
lines changed

core/vm/analysis.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,21 @@ func (bits *bitvec) codeSegment(pos uint64) bool {
6161
}
6262

6363
// codeBitmap collects data locations in code.
64-
func codeBitmap(code []byte) bitvec {
64+
func codeBitmap(c *Contract) bitvec {
6565
// The bitmap is 4 bytes longer than necessary, in case the code
6666
// ends with a PUSH32, the algorithm will push zeroes onto the
6767
// bitvector outside the bounds of the actual code.
68-
bits := make(bitvec, len(code)/8+1+4)
69-
return codeBitmapInternal(code, bits)
68+
bits := make(bitvec, c.CodeSize()/8+1+4)
69+
return codeBitmapInternal(c, bits)
7070
}
7171

7272
// codeBitmapInternal is the internal implementation of codeBitmap.
7373
// It exists for the purpose of being able to run benchmark tests
7474
// without dynamic allocations affecting the results.
75-
func codeBitmapInternal(code, bits bitvec) bitvec {
76-
for pc := uint64(0); pc < uint64(len(code)); {
77-
op := OpCode(code[pc])
75+
func codeBitmapInternal(c *Contract, bits bitvec) bitvec {
76+
codeBegin := c.CodeBeginOffset()
77+
for pc := uint64(0); pc < c.CodeSize(); {
78+
op := OpCode(c.Code[codeBegin+pc])
7879
pc++
7980
if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false).
8081
continue

core/vm/analysis_test.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"math/bits"
2121
"testing"
2222

23+
"github.com/ethereum/go-ethereum/common"
2324
"github.com/ethereum/go-ethereum/crypto"
2425
)
2526

@@ -52,7 +53,15 @@ func TestJumpDestAnalysis(t *testing.T) {
5253
{[]byte{byte(PUSH32)}, 0b0000_0001, 4},
5354
}
5455
for i, test := range tests {
55-
ret := codeBitmap(test.code)
56+
var (
57+
contract Contract
58+
addr common.Address
59+
hash common.Hash
60+
header EOF1Header
61+
)
62+
contract.SetCallCode(&addr, hash, test.code, &header)
63+
64+
ret := codeBitmap(&contract)
5665
if ret[test.which] != test.exp {
5766
t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which])
5867
}
@@ -64,10 +73,17 @@ const analysisCodeSize = 1200 * 1024
6473
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
6574
// 1.4 ms
6675
code := make([]byte, analysisCodeSize)
67-
bench.SetBytes(analysisCodeSize)
76+
var (
77+
contract Contract
78+
addr common.Address
79+
hash common.Hash
80+
header EOF1Header
81+
)
82+
contract.SetCallCode(&addr, hash, code, &header)
6883
bench.ResetTimer()
84+
bench.SetBytes(analysisCodeSize)
6985
for i := 0; i < bench.N; i++ {
70-
codeBitmap(code)
86+
codeBitmap(&contract)
7187
}
7288
bench.StopTimer()
7389
}
@@ -83,20 +99,27 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
8399
}
84100

85101
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
86-
var op OpCode
102+
var (
103+
contract Contract
104+
addr common.Address
105+
hash common.Hash
106+
header EOF1Header
107+
op OpCode
108+
)
87109
bencher := func(b *testing.B) {
88110
code := make([]byte, analysisCodeSize)
89111
b.SetBytes(analysisCodeSize)
90112
for i := range code {
91113
code[i] = byte(op)
92114
}
93115
bits := make(bitvec, len(code)/8+1+4)
116+
contract.SetCallCode(&addr, hash, code, &header)
94117
b.ResetTimer()
95118
for i := 0; i < b.N; i++ {
96119
for j := range bits {
97120
bits[j] = 0
98121
}
99-
codeBitmapInternal(code, bits)
122+
codeBitmapInternal(&contract, bits)
100123
}
101124
}
102125
for op = PUSH1; op <= PUSH32; op++ {

core/vm/contract.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
8888
udest, overflow := dest.Uint64WithOverflow()
8989
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
9090
// Don't bother checking for JUMPDEST in that case.
91-
if overflow || udest >= uint64(len(c.Code)) {
91+
if overflow || udest >= c.CodeSize() {
9292
return false
9393
}
9494
// Only JUMPDESTs allowed for destinations
95-
if OpCode(c.Code[udest]) != JUMPDEST {
95+
if OpCode(c.Code[c.CodeBeginOffset()+udest]) != JUMPDEST {
9696
return false
9797
}
9898
return c.isCode(udest)
@@ -114,7 +114,7 @@ func (c *Contract) isCode(udest uint64) bool {
114114
if !exist {
115115
// Do the analysis and save in parent context
116116
// We do not need to store it in c.analysis
117-
analysis = codeBitmap(c.Code)
117+
analysis = codeBitmap(c)
118118
c.jumpdests[c.CodeHash] = analysis
119119
}
120120
// Also stash it in current contract for faster access
@@ -126,7 +126,7 @@ func (c *Contract) isCode(udest uint64) bool {
126126
// we don't have to recalculate it for every JUMP instruction in the execution
127127
// However, we don't save it within the parent context
128128
if c.analysis == nil {
129-
c.analysis = codeBitmap(c.Code)
129+
c.analysis = codeBitmap(c)
130130
}
131131
return c.analysis.codeSegment(udest)
132132
}
@@ -201,6 +201,13 @@ func (c *Contract) CodeEndOffset() uint64 {
201201
return c.header.CodeEndOffset()
202202
}
203203

204+
func (c *Contract) CodeSize() uint64 {
205+
if c.IsLegacy() {
206+
return uint64(len(c.Code))
207+
}
208+
return uint64(c.header.codeSize)
209+
}
210+
204211
// SetCallCode sets the code of the contract and address of the backing data
205212
// object
206213
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, header *EOF1Header) {

0 commit comments

Comments
 (0)