diff --git a/pkg/grpc/exec/exec.go b/pkg/grpc/exec/exec.go index 1e24f86e1fe..f0da5858dcc 100644 --- a/pkg/grpc/exec/exec.go +++ b/pkg/grpc/exec/exec.go @@ -406,7 +406,7 @@ func GetProcessExit(event *MsgExitEventUnix) *tetragon.ProcessExit { ec.Add(nil, tetragonEvent, event.Common.Ktime, event.ProcessKey.Ktime, event) return nil } - if parent != nil { + if !proc.HasCustomRefCnt() && parent != nil { parent.RefDec("parent") } if proc != nil { @@ -427,16 +427,22 @@ func (msg *MsgExitEventUnix) Notify() bool { func (msg *MsgExitEventUnix) RetryInternal(ev notify.Event, timestamp uint64) (*process.ProcessInternal, error) { internal, parent := process.GetParentProcessInternal(msg.ProcessKey.Pid, timestamp) var err error - - if parent != nil { - ev.SetParent(parent.UnsafeGetProcess()) - if !msg.RefCntDone[ParentRefCnt] { - parent.RefDec("parent") - msg.RefCntDone[ParentRefCnt] = true + hasCustomeRefCnt := false + if internal != nil && internal.HasCustomRefCnt() { + hasCustomeRefCnt = true + } + + if !hasCustomeRefCnt { + if parent != nil { + ev.SetParent(parent.UnsafeGetProcess()) + if !msg.RefCntDone[ParentRefCnt] { + parent.RefDec("parent") + msg.RefCntDone[ParentRefCnt] = true + } + } else { + eventcache.CacheRetries(eventcache.ParentInfo).Inc() + err = eventcache.ErrFailedToGetParentInfo } - } else { - eventcache.CacheRetries(eventcache.ParentInfo).Inc() - err = eventcache.ErrFailedToGetParentInfo } if internal != nil { @@ -494,15 +500,21 @@ func (msg *MsgProcessCleanupEventUnix) Notify() bool { func (msg *MsgProcessCleanupEventUnix) RetryInternal(_ notify.Event, timestamp uint64) (*process.ProcessInternal, error) { internal, parent := process.GetParentProcessInternal(msg.PID, timestamp) var err error - - if parent != nil { - if !msg.RefCntDone[ParentRefCnt] { - parent.RefDec("parent") - msg.RefCntDone[ParentRefCnt] = true + hasCustomeRefCnt := false + if internal != nil && internal.HasCustomRefCnt() { + hasCustomeRefCnt = true + } + + if !hasCustomeRefCnt { + if parent != nil { + if !msg.RefCntDone[ParentRefCnt] { + parent.RefDec("parent") + msg.RefCntDone[ParentRefCnt] = true + } + } else { + eventcache.CacheRetries(eventcache.ParentInfo).Inc() + err = eventcache.ErrFailedToGetParentInfo } - } else { - eventcache.CacheRetries(eventcache.ParentInfo).Inc() - err = eventcache.ErrFailedToGetParentInfo } if internal != nil { @@ -528,7 +540,9 @@ func (msg *MsgProcessCleanupEventUnix) Retry(_ *process.ProcessInternal, _ notif func (msg *MsgProcessCleanupEventUnix) HandleMessage() *tetragon.GetEventsResponse { msg.RefCntDone = [2]bool{false, false} if process, parent := process.GetParentProcessInternal(msg.PID, msg.Ktime); process != nil && parent != nil { - parent.RefDec("parent") + if !process.HasCustomRefCnt() { + parent.RefDec("parent") + } process.RefDec("process") } else { if ec := eventcache.Get(); ec != nil { diff --git a/pkg/process/cache.go b/pkg/process/cache.go index cd6fd250010..e5cfa3bacc5 100644 --- a/pkg/process/cache.go +++ b/pkg/process/cache.go @@ -139,6 +139,10 @@ func (pc *Cache) cleanStaleEntries() { } for _, d := range deleteProcesses { processCacheRemovedStale.Inc() + parent, err := pc.get(d.process.ParentExecId) + if err != nil { + parent.RefDec("parent") + } pc.remove(d.process) } } @@ -159,6 +163,13 @@ func processOrChildExit(reason string) bool { return false } +func processOrChildExec(reason string) bool { + if reason == "process++" || reason == "parent++" { + return true + } + return false +} + func (pc *Cache) deletePending(process *ProcessInternal) { pc.deleteChan <- process } @@ -176,6 +187,14 @@ func (pc *Cache) refDec(p *ProcessInternal, reason string) { ref := atomic.AddUint32(&p.refcnt, ^uint32(0)) if ref == 0 { pc.deletePending(p) + // If the process has a custom refcnt then we need to reduce the parent's + // refcnt now we're finally deleting the process. + if p.HasCustomRefCnt() { + parent, err := pc.get(p.process.ParentExecId) + if err != nil { + parent.RefDec("parent") + } + } } } @@ -183,6 +202,10 @@ func (pc *Cache) refInc(p *ProcessInternal, reason string) { p.refcntOpsLock.Lock() // count number of times refcnt is increamented for a specific reason (i.e. process, parent, etc.) p.refcntOps[reason]++ + // Check if this is a custom refcnt. If so, set the flag. + if !processOrChildExec(reason) { + p.customRefcnt = true + } p.refcntOpsLock.Unlock() atomic.AddUint32(&p.refcnt, 1) } diff --git a/pkg/process/process.go b/pkg/process/process.go index 4524c0bb7ab..50262c2c967 100644 --- a/pkg/process/process.go +++ b/pkg/process/process.go @@ -65,6 +65,16 @@ type ProcessInternal struct { cgID uint64 // the time the process and all children exited exitTime time.Time + // Whether this process has custom refcnts (not "process" or "parent"). + // This cascades on fork, but is reset on exec. The purpose is to delay reducing the + // refcnt on a processes' parent on exit, to when the process itself has a refcnt of 0. + // The result is that any process that has this flag set will keep itself and all its + // forked (but not exec) descendants in the cache after they have exited, until all the + // (non-exec) descendants have exited (as any forked descendant could potentially cause + // an action that is attributed to the process and we need it in the cache in order to + // report it). This serves to prevent entries being removed by the stale entry GC until + // all (non-exec) descendants have exited. + customRefcnt bool } var ( @@ -123,6 +133,7 @@ func (pi *ProcessInternal) cloneInternalProcessCopy() *ProcessInternal { namespaces: pi.namespaces, refcnt: 1, // Explicitly initialize refcnt to 1 refcntOps: map[string]int32{"process++": 1}, + customRefcnt: pi.customRefcnt, } } @@ -149,6 +160,10 @@ func (pi *ProcessInternal) GetCgID() uint64 { return pi.cgID } +func (pi *ProcessInternal) HasCustomRefCnt() bool { + return pi.customRefcnt +} + // UpdateExecOutsideCache() checks if we must augment the ProcessExec.Process // with more fields without propagating again those fields into the process // cache. This means that those added fields will only show up for the @@ -401,6 +416,7 @@ func initProcessInternalExec( refcnt: 1, cgID: event.Kube.Cgrpid, refcntOps: map[string]int32{"process++": 1}, + customRefcnt: false, } }