Skip to content

Commit 6a0c8a7

Browse files
committed
support more than 64 rules with overflow
1 parent 61c1d5d commit 6a0c8a7

9 files changed

Lines changed: 356 additions & 140 deletions

File tree

pkg/ebpf/c/common/filtering.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,19 +385,19 @@ statfunc bool evaluate_scope_filters(program_data_t *p)
385385
{
386386
u64 matched_scope_filters = match_scope_filters(p);
387387
p->event->context.active_rules &= matched_scope_filters;
388-
return p->event->context.active_rules != 0;
388+
return p->event->context.active_rules != 0 || p->event->config.has_overflow;
389389
}
390390

391391
statfunc bool evaluate_data_filters(program_data_t *p, u8 index)
392392
{
393393
u64 matched_data_filters = match_data_filters(p, index);
394394
p->event->context.active_rules &= matched_data_filters;
395-
return p->event->context.active_rules != 0;
395+
return p->event->context.active_rules != 0 || p->event->config.has_overflow;
396396
}
397397

398398
statfunc bool rules_matched(event_data_t *event)
399399
{
400-
return event->context.active_rules != 0;
400+
return event->context.active_rules != 0 || event->config.has_overflow;
401401
}
402402

403403
statfunc bool event_is_selected(u32 event_id)

pkg/ebpf/c/types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,8 @@ typedef struct data_filter_config {
373373

374374
typedef struct event_config {
375375
u16 rules_version;
376-
u8 _padding[6];
376+
u8 has_overflow;
377+
u8 _padding[5];
377378
u64 submit_for_rules;
378379
u64 field_types;
379380
scope_filters_config_t scope_filters;

pkg/ebpf/events_pipeline.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,8 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch
275275
evt.ProcessEntityId = utils.HashTaskID(eCtx.HostPid, normalizedLeaderStartTime)
276276
evt.ParentEntityId = utils.HashTaskID(eCtx.HostPpid, normalizedParentStartTime)
277277

278-
// derived events will match since there exist a dependency rule for them
279-
// TODO: verify the case where there are filters in userspace - they shouldn't clear dependency rule bit
280-
if t.matchRules(evt) == 0 {
278+
// TODO(unrelated): move this to process stage (why did it moved here in the first place?)
279+
if !t.matchRules(evt) {
281280
_ = t.stats.EventsFiltered.Increment()
282281
t.eventsPool.Put(evt)
283282
continue
@@ -298,12 +297,16 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch
298297
// not match the event after userland filters are applied. In those cases, the rule bit is cleared
299298
// (so the event is "filtered" for that rule). This may be called in different stages of the
300299
// pipeline (decode, derive, engine).
301-
func (t *Tracee) matchRules(event *trace.Event) uint64 {
300+
func (t *Tracee) matchRules(event *trace.Event) bool {
302301
eventID := events.ID(event.EventID)
303302
bitmap := event.MatchedRulesKernel
304303

305304
// range through each userland filterable rule
306305
for _, rule := range t.policyManager.GetUserlandRules(eventID) {
306+
if rule.ID > 63 {
307+
continue // we currently don't support filtering for rules with ID > 63
308+
}
309+
307310
// Rule ID is the bit offset in the bitmap.
308311
bitOffset := uint(rule.ID)
309312

@@ -380,7 +383,7 @@ func (t *Tracee) matchRules(event *trace.Event) uint64 {
380383

381384
event.MatchedRulesUser = bitmap // store filtered bitmap to be used in sink stage
382385

383-
return bitmap
386+
return bitmap > 0 || t.policyManager.HasOverflowRules(eventID)
384387
}
385388

386389
func parseContextFlags(containerId string, flags uint32) trace.ContextFlags {
@@ -433,7 +436,7 @@ func (t *Tracee) processEvents(ctx context.Context, in <-chan *trace.Event) (
433436

434437
// Get a bitmap with all rules containing container filters
435438
eventId := events.ID(event.EventID)
436-
rulesWithContainerFilter := t.policyManager.GetContainerFilteredRulesBitmap(eventId)
439+
containerFilteredRules := t.policyManager.GetContainerFilteredRulesBitmap(eventId)
437440

438441
// Filter out events that don't have a container ID from all the rules that
439442
// have container filters. This will guarantee that any of those rules
@@ -444,7 +447,7 @@ func (t *Tracee) processEvents(ctx context.Context, in <-chan *trace.Event) (
444447
// enabled, so, in those cases, ignore the event IF the event is not a
445448
// cgroup_mkdir or cgroup_rmdir.
446449

447-
if rulesWithContainerFilter > 0 && event.Container.ID == "" {
450+
if len(containerFilteredRules) > 0 && containerFilteredRules[0] > 0 && event.Container.ID == "" {
448451
// never skip cgroup_{mkdir,rmdir}: container_{create,remove} events need it
449452
if eventId == events.CgroupMkdir || eventId == events.CgroupRmdir {
450453
goto sendEvent
@@ -454,8 +457,8 @@ func (t *Tracee) processEvents(ctx context.Context, in <-chan *trace.Event) (
454457
"eventId", eventId)
455458

456459
// remove event from rules with container filters
457-
utils.ClearBits(&event.MatchedRulesKernel, rulesWithContainerFilter)
458-
utils.ClearBits(&event.MatchedRulesUser, rulesWithContainerFilter)
460+
utils.ClearBits(&event.MatchedRulesKernel, containerFilteredRules[0])
461+
utils.ClearBits(&event.MatchedRulesUser, containerFilteredRules[0])
459462

460463
if event.MatchedRulesKernel == 0 {
461464
t.eventsPool.Put(event)
@@ -494,6 +497,9 @@ func (t *Tracee) deriveEvents(ctx context.Context, in <-chan *trace.Event) (
494497
continue // might happen during initialization (ctrl+c seg faults)
495498
}
496499

500+
// TODO (unrelated): avoid any copy if there are no derivations for the event
501+
// This can be done by adding a new function in derive package to check if derived events exist
502+
497503
// Get a copy of our event before sending it down the pipeline. This is
498504
// needed because later modification of the event (in particular of the
499505
// matched rules) can affect the derivation and later pipeline logic
@@ -539,7 +545,7 @@ func (t *Tracee) deriveEvents(ctx context.Context, in <-chan *trace.Event) (
539545
case events.PrintMemDump:
540546
default:
541547
// Derived events might need filtering as well
542-
if t.matchRules(event) == 0 {
548+
if !t.matchRules(event) {
543549
_ = t.stats.EventsFiltered.Increment()
544550
continue
545551
}
@@ -580,8 +586,8 @@ func (t *Tracee) sinkEvents(ctx context.Context, in <-chan *trace.Event) <-chan
580586

581587
// Only emit events requested by the user and matched by at least one rule.
582588
id := events.ID(event.EventID)
583-
event.MatchedRulesUser, event.MatchedPolicies = t.policyManager.GetMatchedRulesInfo(id, event.MatchedRulesUser)
584-
if event.MatchedRulesUser == 0 {
589+
event.MatchedPolicies = t.policyManager.GetMatchedRulesInfo(id, event.MatchedRulesUser)
590+
if len(event.MatchedPolicies) == 0 {
585591
t.eventsPool.Put(event)
586592
continue
587593
}

pkg/ebpf/signature_engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func (t *Tracee) engineEvents(ctx context.Context, in <-chan *trace.Event) (<-ch
143143
continue
144144
}
145145

146-
if t.matchRules(event) == 0 {
146+
if !t.matchRules(event) {
147147
_ = t.stats.EventsFiltered.Increment()
148148
continue
149149
}

pkg/ebpf/tracee.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,8 @@ func (t *Tracee) invokeInitEvents(out chan *trace.Event) {
16111611
setMatchedRules := func(event *trace.Event, matchedRules uint64) {
16121612
event.RulesVersion = 1 // version will be removed soon
16131613
event.MatchedRulesKernel = matchedRules
1614-
event.MatchedRulesUser, event.MatchedPolicies = t.policyManager.GetMatchedRulesInfo(events.ID(event.EventID), matchedRules)
1614+
event.MatchedRulesUser = matchedRules
1615+
event.MatchedPolicies = t.policyManager.GetMatchedRulesInfo(events.ID(event.EventID), matchedRules)
16151616
}
16161617

16171618
rulesMatch := func(id events.ID) uint64 {

pkg/policy/ebpf.go

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,28 @@ func (pm *PolicyManager) updateBPF(
119119
return nil
120120
}
121121

122+
// eBPF data filter only supports first 64 rules for each key.
123+
type stringFilterConfigBPF struct {
124+
prefixEnabled uint64
125+
suffixEnabled uint64
126+
exactEnabled uint64
127+
prefixMatchIfKeyMissing uint64
128+
suffixMatchIfKeyMissing uint64
129+
exactMatchIfKeyMissing uint64
130+
}
131+
132+
type dataFilterConfigBPF struct {
133+
string stringFilterConfigBPF
134+
}
135+
122136
type eventConfig struct {
123137
rulesVersion uint16
124-
padding [6]uint8 // free for further use
138+
hasOverflow uint8
139+
padding [5]uint8 // free for further use
125140
submitForRules uint64
126141
fieldTypes uint64
127142
scopeFilters scopeFiltersConfig
128-
dataFilter dataFilterConfig
143+
dataFilter dataFilterConfigBPF
129144
}
130145

131146
// updateEventsConfigMap updates the events config map with the given events fields and filter config.
@@ -145,6 +160,27 @@ func (pm *PolicyManager) updateEventsConfigMap(
145160
filterConfig = dataFilterConfig{}
146161
}
147162

163+
// Extract the first bitmap from each field of stringFilterConfig
164+
dataFilterCfg := dataFilterConfigBPF{}
165+
if len(filterConfig.string.prefixEnabled) > 0 {
166+
dataFilterCfg.string.prefixEnabled = filterConfig.string.prefixEnabled[0]
167+
}
168+
if len(filterConfig.string.suffixEnabled) > 0 {
169+
dataFilterCfg.string.suffixEnabled = filterConfig.string.suffixEnabled[0]
170+
}
171+
if len(filterConfig.string.exactEnabled) > 0 {
172+
dataFilterCfg.string.exactEnabled = filterConfig.string.exactEnabled[0]
173+
}
174+
if len(filterConfig.string.prefixMatchIfKeyMissing) > 0 {
175+
dataFilterCfg.string.prefixMatchIfKeyMissing = filterConfig.string.prefixMatchIfKeyMissing[0]
176+
}
177+
if len(filterConfig.string.suffixMatchIfKeyMissing) > 0 {
178+
dataFilterCfg.string.suffixMatchIfKeyMissing = filterConfig.string.suffixMatchIfKeyMissing[0]
179+
}
180+
if len(filterConfig.string.exactMatchIfKeyMissing) > 0 {
181+
dataFilterCfg.string.exactMatchIfKeyMissing = filterConfig.string.exactMatchIfKeyMissing[0]
182+
}
183+
148184
// encoded event's field types
149185
var fieldTypes uint64
150186
fields := eventsFields[id]
@@ -154,16 +190,25 @@ func (pm *PolicyManager) updateEventsConfigMap(
154190

155191
// Create submit bitmap based on rules count - n least significant bits set to 1
156192
submitForRules := uint64(0)
157-
if ecfg.rulesCount > 0 {
193+
if ecfg.rulesCount >= 64 {
194+
submitForRules = ^uint64(0) // All bits set to 1
195+
} else if ecfg.rulesCount > 0 {
158196
submitForRules = (uint64(1) << ecfg.rulesCount) - 1
159197
}
160198

199+
// Set hasOverflow flag
200+
var overflowFlag uint8
201+
if ecfg.hasOverflow {
202+
overflowFlag = 1
203+
}
204+
161205
eventConfig := eventConfig{
162206
rulesVersion: ecfg.rulesVersion,
207+
hasOverflow: overflowFlag,
163208
submitForRules: submitForRules,
164209
fieldTypes: fieldTypes,
165210
scopeFilters: pm.computeScopeFiltersConfig(id),
166-
dataFilter: filterConfig,
211+
dataFilter: dataFilterCfg,
167212
}
168213

169214
err := eventsConfigMap.Update(unsafe.Pointer(&id), unsafe.Pointer(&eventConfig))
@@ -218,8 +263,8 @@ func (pm *PolicyManager) computeScopeFiltersConfig(eventID events.ID) scopeFilte
218263

219264
// Loop through rules for this event
220265
for _, rule := range eventRules.Rules {
221-
if rule.Policy == nil {
222-
continue // Skip dependency rules that have no policy
266+
if rule.Policy == nil || rule.ID >= 64 {
267+
continue
223268
}
224269

225270
offset := rule.ID // Use rule ID (0-63) for bitmap position
@@ -301,7 +346,7 @@ func (pm *PolicyManager) computeScopeFiltersConfig(eventID events.ID) scopeFilte
301346
// updateUIntFilterBPF updates the BPF maps for the given uint filter map.
302347
func (pm *PolicyManager) updateUIntFilterBPF(
303348
bpfModule *bpf.Module,
304-
filterMap map[filterVersionKey]map[uint64]ruleBitmap,
349+
filterMap map[filterVersionKey]map[uint64][]ruleBitmap,
305350
innerMapName string,
306351
outerMapName string,
307352
) error {
@@ -318,7 +363,15 @@ func (pm *PolicyManager) updateUIntFilterBPF(
318363
vKey.Version, vKey.EventID, err)
319364
}
320365

321-
for key, bitmap := range innerMap {
366+
for key, bitmaps := range innerMap {
367+
// Check if there are bitmaps for this key
368+
if len(bitmaps) == 0 {
369+
continue
370+
}
371+
372+
// Update only the first bitmap (first 64 rules)
373+
bitmap := bitmaps[0]
374+
322375
// Convert the uint64 key to []byte
323376
keyBytes := make([]byte, 4)
324377
binary.LittleEndian.PutUint32(keyBytes, uint32(key))
@@ -343,7 +396,7 @@ func (pm *PolicyManager) updateUIntFilterBPF(
343396
// updateStringFilterBPF updates the BPF maps for the given string filter map.
344397
func (pm *PolicyManager) updateStringFilterBPF(
345398
bpfModule *bpf.Module,
346-
filterMap map[filterVersionKey]map[string]ruleBitmap,
399+
filterMap map[filterVersionKey]map[string][]ruleBitmap,
347400
innerMapName string,
348401
outerMapName string,
349402
) error {
@@ -360,7 +413,15 @@ func (pm *PolicyManager) updateStringFilterBPF(
360413
vKey.Version, vKey.EventID, err)
361414
}
362415

363-
for key, bitmap := range innerMap {
416+
for key, bitmaps := range innerMap {
417+
// Check if there are bitmaps for this key
418+
if len(bitmaps) == 0 {
419+
continue
420+
}
421+
422+
// Update only the first bitmap (first 64 rules)
423+
bitmap := bitmaps[0]
424+
364425
byteStr := make([]byte, maxBpfStrFilterSize)
365426
copy(byteStr, key)
366427
keyPointer := unsafe.Pointer(&byteStr[0])
@@ -383,7 +444,7 @@ func (pm *PolicyManager) updateStringFilterBPF(
383444
// updateBinaryFilterBPF updates the BPF maps for the given binary filter map.
384445
func (pm *PolicyManager) updateBinaryFilterBPF(
385446
bpfModule *bpf.Module,
386-
filterMap map[filterVersionKey]map[filters.NSBinary]ruleBitmap,
447+
filterMap map[filterVersionKey]map[filters.NSBinary][]ruleBitmap,
387448
innerMapName string,
388449
outerMapName string,
389450
) error {
@@ -400,7 +461,15 @@ func (pm *PolicyManager) updateBinaryFilterBPF(
400461
vKey.Version, vKey.EventID, err)
401462
}
402463

403-
for key, bitmap := range innerMap {
464+
for key, bitmaps := range innerMap {
465+
// Check if there are bitmaps for this key
466+
if len(bitmaps) == 0 {
467+
continue
468+
}
469+
470+
// Update only the first bitmap (first 64 rules)
471+
bitmap := bitmaps[0]
472+
404473
if len(key.Path) > maxBpfBinPathSize {
405474
return filters.InvalidValue(key.Path)
406475
}
@@ -434,7 +503,7 @@ func (pm *PolicyManager) updateBinaryFilterBPF(
434503
// updateStringDataFilterLPMBPF updates the BPF maps for the given kernel data LPM filter map.
435504
func (pm *PolicyManager) updateStringDataFilterLPMBPF(
436505
bpfModule *bpf.Module,
437-
filterMap map[filterVersionKey]map[string]ruleBitmap,
506+
filterMap map[filterVersionKey]map[string][]ruleBitmap,
438507
innerMapName string,
439508
outerMapName string,
440509
) error {
@@ -451,7 +520,15 @@ func (pm *PolicyManager) updateStringDataFilterLPMBPF(
451520
vKey.Version, vKey.EventID, err)
452521
}
453522

454-
for key, bitmap := range innerMap {
523+
for key, bitmaps := range innerMap {
524+
// Check if there are bitmaps for this key
525+
if len(bitmaps) == 0 {
526+
continue
527+
}
528+
529+
// Update only the first bitmap (first 64 rules)
530+
bitmap := bitmaps[0]
531+
455532
// Ensure the string length is within the maximum allowed limit,
456533
// excluding the NULL terminator.
457534
if len(key) > maxBpfDataFilterStrSize-1 {
@@ -484,7 +561,7 @@ func (pm *PolicyManager) updateStringDataFilterLPMBPF(
484561
// updateStringDataFilterBPF updates the BPF maps for the given kernel data filter map.
485562
func (pm *PolicyManager) updateStringDataFilterBPF(
486563
bpfModule *bpf.Module,
487-
filterMap map[filterVersionKey]map[string]ruleBitmap,
564+
filterMap map[filterVersionKey]map[string][]ruleBitmap,
488565
innerMapName string,
489566
outerMapName string,
490567
) error {
@@ -501,7 +578,15 @@ func (pm *PolicyManager) updateStringDataFilterBPF(
501578
vKey.Version, vKey.EventID, err)
502579
}
503580

504-
for key, bitmap := range innerMap {
581+
for key, bitmaps := range innerMap {
582+
// Check if there are bitmaps for this key
583+
if len(bitmaps) == 0 {
584+
continue
585+
}
586+
587+
// Update only the first bitmap (first 64 rules)
588+
bitmap := bitmaps[0]
589+
505590
// Ensure the string length is within the maximum allowed limit,
506591
// excluding the NULL terminator
507592
if len(key) > maxBpfDataFilterStrSize-1 {
@@ -636,7 +721,7 @@ type procInfo struct {
636721
// populateProcInfoMap populates the ProcInfoMap with the binaries to track.
637722
// TODO: Should ProcInfoMap be cleared when a Policies new version is created?
638723
// Or should it be versioned too?
639-
func populateProcInfoMap(bpfModule *bpf.Module, filterMap map[filterVersionKey]map[filters.NSBinary]ruleBitmap) error {
724+
func populateProcInfoMap(bpfModule *bpf.Module, filterMap map[filterVersionKey]map[filters.NSBinary][]ruleBitmap) error {
640725
procInfoMap, err := bpfModule.GetMap(ProcInfoMap)
641726
if err != nil {
642727
return errfmt.WrapError(err)

pkg/policy/errors.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ func PolicyNotFoundByNameError(name string) error {
3232
return &policyError{msg: fmt.Sprintf("policy [%s] not found", name)}
3333
}
3434

35-
func TooManyRulesForEventError(eventName string) error {
36-
return &policyError{msg: fmt.Sprintf("too many rules for event %s", eventName)}
37-
}
38-
3935
func SelectEventError(eventName string) error {
4036
return &policyError{msg: fmt.Sprintf("failed to select event %s", eventName)}
4137
}

0 commit comments

Comments
 (0)