Every tracer that implements Stop/GetResult held a `reason error`
field that is written by Stop (called from the trace-timeout watchdog
goroutine in api.go) and read by GetResult (called by the RPC handler
main goroutine). These accesses were unsynchronized.
Because Go's error is an interface, a racing reader can observe a
torn eface (type pointer and data pointer from different stores),
which is undefined behaviour and can crash the node.
Replace the plain field with atomic.Pointer[error] in:
- eth/tracers/native/call.go (callTracer)
- eth/tracers/native/4byte.go (fourByteTracer)
- eth/tracers/native/erc7562.go (erc7562Tracer)
- eth/tracers/native/prestate.go (prestateTracer)
- eth/tracers/logger/logger.go (StructLogger)
The flatCallTracer shares callTracer.reason and is fixed transitively.
Add TestTracerStopRace which exercises the concurrent Stop/GetResult
path under -race for each of the five tracers. It fails on master
with the race detector, passes after the fix.