Skip to content

Commit b72c76e

Browse files
gzliudans1naholiman
authored
eth/tracers: pad memory slice on OOB case ethereum#25213 (#1295)
* eth/tracers: pad memory slice on oob case * eth/tracers/js: fix testfailure due to err msg capitalization Co-authored-by: Sina Mahmoodi <[email protected]> Co-authored-by: Martin Holst Swende <[email protected]>
1 parent 98be4bb commit b72c76e

File tree

2 files changed

+42
-13
lines changed

2 files changed

+42
-13
lines changed

eth/tracers/js/goja.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ import (
3232
"github.com/dop251/goja"
3333
)
3434

35+
const (
36+
memoryPadLimit = 1024 * 1024
37+
)
38+
3539
var assetTracers = make(map[string]string)
3640

3741
// init retrieves the JavaScript transaction tracers included in go-ethereum.
@@ -561,10 +565,15 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
561565
if end < begin || begin < 0 {
562566
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
563567
}
564-
if mo.memory.Len() < int(end) {
565-
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
568+
mlen := mo.memory.Len()
569+
if end-int64(mlen) > memoryPadLimit {
570+
return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen)
566571
}
567-
return mo.memory.GetCopy(begin, end-begin), nil
572+
slice := make([]byte, end-begin)
573+
end = min(end, int64(mo.memory.Len()))
574+
ptr := mo.memory.GetPtr(begin, end-begin)
575+
copy(slice[:], ptr[:])
576+
return slice, nil
568577
}
569578

570579
func (mo *memoryObj) GetUint(addr int64) goja.Value {
@@ -944,3 +953,10 @@ func (l *steplog) setupObject() *goja.Object {
944953
o.Set("contract", l.contract.setupObject())
945954
return o
946955
}
956+
957+
func min(a, b int64) int64 {
958+
if a < b {
959+
return a
960+
}
961+
return b
962+
}

eth/tracers/js/tracer_test.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func testCtx() *vmContext {
6060
return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}}
6161
}
6262

63-
func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
63+
func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
6464
var (
6565
env = vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer})
6666
gasLimit uint64 = 31000
@@ -69,6 +69,9 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
6969
contract = vm.NewContract(account{}, account{}, value, startGas)
7070
)
7171
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
72+
if contractCode != nil {
73+
contract.Code = contractCode
74+
}
7275

7376
tracer.CaptureTxStart(gasLimit)
7477
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
@@ -83,22 +86,23 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
8386
}
8487

8588
func TestTracer(t *testing.T) {
86-
execTracer := func(code string) ([]byte, string) {
89+
execTracer := func(code string, contract []byte) ([]byte, string) {
8790
t.Helper()
8891
tracer, err := newJsTracer(code, nil, nil)
8992
if err != nil {
9093
t.Fatal(err)
9194
}
92-
ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
95+
ret, err := runTrace(tracer, testCtx(), params.TestChainConfig, contract)
9396
if err != nil {
9497
return nil, err.Error() // Stringify to allow comparison without nil checks
9598
}
9699
return ret, ""
97100
}
98101
for i, tt := range []struct {
99-
code string
100-
want string
101-
fail string
102+
code string
103+
want string
104+
fail string
105+
contract []byte
102106
}{
103107
{ // tests that we don't panic on bad arguments to memory access
104108
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
@@ -139,9 +143,18 @@ func TestTracer(t *testing.T) {
139143
}, {
140144
code: "{res: null, step: function(log) { var address = Array.prototype.slice.call(log.contract.getAddress()); this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}",
141145
want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`,
146+
}, {
147+
code: "{res: [], step: function(log) { var op = log.op.toString(); if (op === 'MSTORE8' || op === 'STOP') { this.res.push(log.memory.slice(0, 2)) } }, fault: function() {}, result: function() { return this.res }}",
148+
want: `[{"0":0,"1":0},{"0":255,"1":0}]`,
149+
contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)},
150+
}, {
151+
code: "{res: [], step: function(log) { if (log.op.toString() === 'STOP') { this.res.push(log.memory.slice(5, 1025 * 1024)) } }, fault: function() {}, result: function() { return this.res }}",
152+
want: "",
153+
fail: "tracer reached limit for padding memory slice: end 1049600, memorySize 32 at step (<eval>:1:83(20)) in server-side tracer function 'step'",
154+
contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)},
142155
},
143156
} {
144-
if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err {
157+
if have, err := execTracer(tt.code, tt.contract); tt.want != string(have) || tt.fail != err {
145158
t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code)
146159
}
147160
}
@@ -157,7 +170,7 @@ func TestHalt(t *testing.T) {
157170
time.Sleep(1 * time.Second)
158171
tracer.Stop(timeout)
159172
}()
160-
if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); !strings.Contains(err.Error(), "stahp") {
173+
if _, err = runTrace(tracer, testCtx(), params.TestChainConfig, nil); !strings.Contains(err.Error(), "stahp") {
161174
t.Errorf("Expected timeout error, got %v", err)
162175
}
163176
}
@@ -244,7 +257,7 @@ func TestIsPrecompile(t *testing.T) {
244257
}
245258

246259
blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150)}
247-
res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
260+
res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
248261
if err != nil {
249262
t.Error(err)
250263
}
@@ -254,7 +267,7 @@ func TestIsPrecompile(t *testing.T) {
254267

255268
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
256269
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
257-
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
270+
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
258271
if err != nil {
259272
t.Error(err)
260273
}

0 commit comments

Comments
 (0)