Skip to content

Commit

Permalink
server: make a copy of exit messages when filtering fields
Browse files Browse the repository at this point in the history
[upstream commit 6b34552]

We encountered another segfault similar to the one fixed in #1432. This time, it's related
to the pid field in exit events. Fix this by making a copy of the exit event before
filtering. Also optimize copying logic to only perform the copy when absolutely necessary.

Signed-off-by: William Findlay <will@isovalent.com>
  • Loading branch information
willfindlay committed Nov 2, 2023
1 parent 697dd77 commit acb11ad
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 17 deletions.
51 changes: 41 additions & 10 deletions pkg/filters/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,48 @@ type FieldFilter struct {
fields fmutils.NestedMask
action tetragon.FieldFilterAction
invertEventSet bool
needsCopy map[tetragon.EventType]struct{}
}

// NewFieldFilter constructs a new FieldFilter from a set of fields.
func NewFieldFilter(eventSet []tetragon.EventType, fields []string, action tetragon.FieldFilterAction, invertEventSet bool) *FieldFilter {
// We only need to copy exec and exit events when they are explicitly filtering out
// the PID. This is because we need the PID to not be nil when accessing the event
// later on from the eventcache. See additional comments below for details.
var maybeNeedsCopy bool
if action == tetragon.FieldFilterAction_EXCLUDE {
for _, field := range fields {
if strings.HasPrefix(field, "process") {
maybeNeedsCopy = true
break
}
}
} else if action == tetragon.FieldFilterAction_INCLUDE {
// For inclusion, it's the opposite situation from the above. If the process.pid
// field is NOT present, it will be trimmed. So assume we need a copy unless we
// see process.pid.
maybeNeedsCopy = true
for _, field := range fields {
if field == "process.pid" {
maybeNeedsCopy = false
break
}
}
}

needsCopy := make(map[tetragon.EventType]struct{})
if maybeNeedsCopy {
for _, t := range eventSet {
needsCopy[t] = struct{}{}
}
}

return &FieldFilter{
eventSet: eventSet,
fields: fmutils.NestedMaskFromPaths(fields),
action: action,
invertEventSet: invertEventSet,
needsCopy: needsCopy,
}
}

Expand All @@ -105,25 +138,18 @@ func NewExcludeFieldFilter(eventSet []tetragon.EventType, fields []string, inver

// FieldFilterFromProto constructs a new FieldFilter from a Tetragon API field filter.
func FieldFilterFromProto(filter *tetragon.FieldFilter) *FieldFilter {
var fields fmutils.NestedMask
var fields []string

if filter.Fields != nil {
fields = fmutils.NestedMaskFromPaths(filter.Fields.Paths)
} else {
fields = make(fmutils.NestedMask)
fields = filter.Fields.Paths
}

invert := false
if filter.InvertEventSet != nil {
invert = filter.InvertEventSet.Value
}

return &FieldFilter{
eventSet: filter.EventSet,
fields: fields,
action: filter.Action,
invertEventSet: invert,
}
return NewFieldFilter(filter.EventSet, fields, filter.Action, invert)
}

// FieldFiltersFromGetEventsRequest returns a list of EventFieldFilter for
Expand All @@ -141,6 +167,11 @@ func FieldFiltersFromGetEventsRequest(request *tetragon.GetEventsRequest) []*Fie
return filters
}

func (f *FieldFilter) NeedsCopy(ev *tetragon.GetEventsResponse) bool {
_, ok := f.needsCopy[ev.EventType()]
return ok
}

// Filter filters the fields in the GetEventsResponse, keeping fields specified in the
// inclusion filter and discarding fields specified in the exclusion filter. Exclusion
// takes precedence over inclusion and an empty filter set will keep all remaining fields.
Expand Down
13 changes: 6 additions & 7 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,13 @@ func (s *Server) GetEventsWG(request *tetragon.GetEventsRequest, server tetragon
// Filter the GetEventsResponse fields
filters := filters.FieldFiltersFromGetEventsRequest(request)
filterEvent := event
if len(filters) > 0 && filterEvent.GetProcessExec() != nil { // this is an exec event and we have fieldFilters
// We need a copy of the exec event as modifing the original message
// can cause issues in the process cache (we keep a copy of that message there).
filterEvent = proto.Clone(event).(*tetragon.GetEventsResponse)
}

for _, filter := range filters {
// we need not to change res
// maybe only for exec events
// We need a copy of the exec or exit event as modifing the original message
// can cause issues in the process cache (we keep a copy of that message there).
if filter.NeedsCopy(filterEvent) && filterEvent == event {
filterEvent = proto.Clone(event).(*tetragon.GetEventsResponse)
}
filter.Filter(filterEvent)
}

Expand Down

0 comments on commit acb11ad

Please sign in to comment.