From c3e65c0e50a9b462f5d0c5b34bd89a1396ca01fa Mon Sep 17 00:00:00 2001 From: mayankpande88 Date: Wed, 15 Jul 2026 14:13:23 +0530 Subject: [PATCH 1/2] fix: guard eBPF map iteration until tracer.Run loads the collection handleEvents starts before tracer.Run so it can drain the events channel while init() replays existing pids. But its 5s ebpfStatsTicker can fire before the eBPF collection is loaded on slow nodes (cold verifier, burstable instances), and updateEbpfStatsAndActiveConns then panics: panic: runtime error: invalid memory address or nil pointer dereference ebpftracer.(*Tracer).ActiveConnectionsIterator(...) containers.(*Registry).updateEbpfStatsAndActiveConns Seen in prod as a burst of restarts (exit code 2) on a spot t3.2xlarge until the node warmed up. Present since the ticker was introduced (#219). Add an atomic ready flag set once the collection is loaded, and skip the stats pass until it is. --- containers/registry.go | 5 +++++ ebpftracer/tracer.go | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/containers/registry.go b/containers/registry.go index 82465fe..b239874 100644 --- a/containers/registry.go +++ b/containers/registry.go @@ -716,6 +716,11 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container { // updateEbpfStatsAndActiveConns reads eBPF maps and updates container stats // directly from the event handler goroutine. Also refreshes active connection gauges. func (r *Registry) updateEbpfStatsAndActiveConns() { + // handleEvents starts before tracer.Run has loaded the eBPF collection; + // iterating its maps before then panics on a nil dereference. + if !r.tracer.Ready() { + return + } // Traffic stats from eBPF maps iter := r.tracer.ActiveConnectionsIterator() cid := ebpftracer.ConnectionId{} diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index 26ec9de..7d3509a 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -104,6 +104,11 @@ type Tracer struct { ringbufReader *ringbuf.Reader // Ring buffer reader for l7_events links []link.Link uprobes map[string]*ebpf.Program + + // ready is set once Run has loaded the eBPF collection. Consumers running + // before Run completes (e.g. the registry event-handler goroutine) must + // check it before touching collection maps. + ready atomic.Bool } func NewTracer(hostNetNs, selfNetNs netns.NsHandle, disableL7Tracing bool) *Tracer { @@ -127,12 +132,19 @@ func (t *Tracer) Run(events chan<- Event) error { if err := t.ebpf(events); err != nil { return err } + t.ready.Store(true) if err := t.init(events); err != nil { return err } return nil } +// Ready reports whether the eBPF collection has been loaded. Map iterators +// must not be used until this returns true. +func (t *Tracer) Ready() bool { + return t.ready.Load() +} + func (t *Tracer) Close() { for _, p := range t.uprobes { _ = p.Close() From a4377cabb45bfb87a14cd803ef79b8823e72aed6 Mon Sep 17 00:00:00 2001 From: mayankpande88 Date: Wed, 15 Jul 2026 14:29:41 +0530 Subject: [PATCH 2/2] review: set ready only after init completes the initial process scan --- ebpftracer/tracer.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index 7d3509a..71370c7 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -105,9 +105,10 @@ type Tracer struct { links []link.Link uprobes map[string]*ebpf.Program - // ready is set once Run has loaded the eBPF collection. Consumers running - // before Run completes (e.g. the registry event-handler goroutine) must - // check it before touching collection maps. + // ready is set once Run has loaded the eBPF collection and completed the + // initial process scan. Consumers running before Run completes (e.g. the + // registry event-handler goroutine) must check it before touching + // collection maps. ready atomic.Bool } @@ -132,15 +133,15 @@ func (t *Tracer) Run(events chan<- Event) error { if err := t.ebpf(events); err != nil { return err } - t.ready.Store(true) if err := t.init(events); err != nil { return err } + t.ready.Store(true) return nil } -// Ready reports whether the eBPF collection has been loaded. Map iterators -// must not be used until this returns true. +// Ready reports whether Run has loaded the eBPF collection and finished the +// initial process scan. Map iterators must not be used until this returns true. func (t *Tracer) Ready() bool { return t.ready.Load() }