eth/tracers/logger: enforce pre-append size limit

Reject struct log entries that would push the buffered trace output past the configured limit.
Add regression tests for oversized entries and exact boundary handling.
This commit is contained in:
Daniel Liu 2026-04-08 18:28:39 +08:00
parent 04e40995d9
commit 85363fb6b8
2 changed files with 45 additions and 4 deletions

View file

@ -276,10 +276,6 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
if l.skip {
return
}
// check if already accumulated the size of the response.
if l.cfg.Limit != 0 && l.resultSize > l.cfg.Limit {
return
}
var (
op = vm.OpCode(opcode)
memory = scope.MemoryData()
@ -329,6 +325,9 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
// create a log
if l.writer == nil {
entry := log.toLegacyJSON()
if l.cfg.Limit != 0 && l.resultSize+len(entry) > l.cfg.Limit {
return
}
l.resultSize += len(entry)
l.logs = append(l.logs, entry)
return

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
@ -43,6 +44,16 @@ func (*dummyStatedb) GetStateAndCommittedState(common.Address, common.Hash) (com
return common.Hash{}, common.Hash{}
}
type dummyOpContext struct{}
func (dummyOpContext) MemoryData() []byte { return nil }
func (dummyOpContext) StackData() []uint256.Int { return nil }
func (dummyOpContext) Caller() common.Address { return common.Address{} }
func (dummyOpContext) Address() common.Address { return common.Address{} }
func (dummyOpContext) CallValue() *uint256.Int { return new(uint256.Int) }
func (dummyOpContext) CallInput() []byte { return nil }
func (dummyOpContext) ContractCode() []byte { return nil }
func TestStoreCapture(t *testing.T) {
var (
logger = NewStructLogger(nil)
@ -139,3 +150,34 @@ func TestStructLogLegacyJSONSpecFormatting(t *testing.T) {
})
}
}
func TestStructLoggerLimitRejectsOversizedEntry(t *testing.T) {
entry := (&StructLog{Op: vm.STOP, RefundCounter: 1337}).toLegacyJSON()
logger := NewStructLogger(&Config{Limit: len(entry) - 1})
logger.env = &tracing.VMContext{StateDB: &dummyStatedb{}}
logger.OnOpcode(0, byte(vm.STOP), 0, 0, dummyOpContext{}, nil, 0, nil)
if len(logger.logs) != 0 {
t.Fatalf("expected oversized entry to be skipped, got %d logs", len(logger.logs))
}
if logger.resultSize != 0 {
t.Fatalf("expected result size to remain zero, got %d", logger.resultSize)
}
}
func TestStructLoggerLimitAllowsEntryUpToBoundary(t *testing.T) {
entry := (&StructLog{Op: vm.STOP, RefundCounter: 1337}).toLegacyJSON()
logger := NewStructLogger(&Config{Limit: len(entry)})
logger.env = &tracing.VMContext{StateDB: &dummyStatedb{}}
logger.OnOpcode(0, byte(vm.STOP), 0, 0, dummyOpContext{}, nil, 0, nil)
logger.OnOpcode(0, byte(vm.STOP), 0, 0, dummyOpContext{}, nil, 0, nil)
if len(logger.logs) != 1 {
t.Fatalf("expected exactly one log at the size boundary, got %d", len(logger.logs))
}
if logger.resultSize != len(entry) {
t.Fatalf("expected result size %d, got %d", len(entry), logger.resultSize)
}
}