Skip to content

Commit 5d1152d

Browse files
authored
[CWS] fix race between active ruleset and replay (#45088)
### What does this PR do? This PR ensures that the replay events are evaluation against the current ruleset loaded. Before the replay events were potentially evaluated against the previous ruleset. ### Motivation ### Describe how you validated your changes ### Additional Notes Co-authored-by: sylvain.afchain <sylvain.afchain@datadoghq.com>
1 parent d9c6ac1 commit 5d1152d

6 files changed

Lines changed: 55 additions & 31 deletions

File tree

pkg/security/probe/probe.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type PlatformProbe interface {
4848
NewModel() *model.Model
4949
DumpDiscarders() (string, error)
5050
FlushDiscarders() error
51-
ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, error)
51+
ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, bool, error)
5252
OnNewRuleSetLoaded(_ *rules.RuleSet)
5353
OnNewDiscarder(_ *rules.RuleSet, _ *model.Event, _ eval.Field, _ eval.EventType)
5454
HandleActions(_ *eval.Context, _ *rules.Rule)
@@ -58,6 +58,7 @@ type PlatformProbe interface {
5858
AddDiscarderPushedCallback(_ DiscarderPushedCallback)
5959
GetEventTags(_ containerutils.ContainerID) []string
6060
EnableEnforcement(bool)
61+
ReplayEvents()
6162
}
6263

6364
var probeTelemetry = struct {
@@ -221,7 +222,7 @@ func (p *Probe) FlushDiscarders() error {
221222
}
222223

223224
// ApplyRuleSet setup the probes for the provided set of rules and returns the policy report.
224-
func (p *Probe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, error) {
225+
func (p *Probe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, bool, error) {
225226
return p.PlatformProbe.ApplyRuleSet(rs)
226227
}
227228

@@ -409,6 +410,11 @@ func (p *Probe) onRuleActionPerformed(rule *rules.Rule, action *rules.ActionDefi
409410
}
410411
}
411412

413+
// ReplayEvents replays the events from the rule set
414+
func (p *Probe) ReplayEvents() {
415+
p.PlatformProbe.ReplayEvents()
416+
}
417+
412418
// NewRuleSet returns a new ruleset
413419
func (p *Probe) NewRuleSet(eventTypeEnabled map[eval.EventType]bool) *rules.RuleSet {
414420
ruleOpts, evalOpts := rules.NewBothOpts(eventTypeEnabled)

pkg/security/probe/probe_ebpf.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,16 @@ func (p *EBPFProbe) Start() error {
802802
return p.eventStream.Start(&p.wg)
803803
}
804804

805+
// ReplayEvents replays the events from the rule set
806+
func (p *EBPFProbe) ReplayEvents() {
807+
p.replayEventsState.Store(true)
808+
809+
// trigger a nop event to ensure that the replayed events are processed
810+
if err := p.triggerNopEvent(); err != nil {
811+
seclog.Warnf("unable to trigger nop event: %v", err)
812+
}
813+
}
814+
805815
func (p *EBPFProbe) replayEvents(notifyConsumers bool) {
806816
seclog.Debugf("playing the snapshot")
807817

@@ -2416,16 +2426,16 @@ func isRawPacketActionPresent(rs *rules.RuleSet) bool {
24162426
}
24172427

24182428
// ApplyRuleSet apply the required update to handle the new ruleset
2419-
func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, error) {
2429+
func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, bool, error) {
24202430
if p.opts.SyscallsMonitorEnabled {
24212431
if err := p.monitors.syscallsMonitor.Disable(); err != nil {
2422-
return nil, err
2432+
return nil, false, err
24232433
}
24242434
}
24252435

24262436
filterReport, err := kfilters.ComputeFilters(p.config.Probe, rs)
24272437
if err != nil {
2428-
return nil, err
2438+
return nil, false, err
24292439
}
24302440

24312441
if err := applyDNSDefaultDropMaskFromRules(p.Manager, rs); err != nil {
@@ -2438,16 +2448,16 @@ func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, err
24382448
if err := p.setApprovers(eventType, report.Approvers); err != nil {
24392449
seclog.Errorf("Error while adding approvers fallback in-kernel policy to `%s` for `%s`: %s", kfilters.PolicyModeAccept, eventType, err)
24402450
if err := fpb.add(eventType, kfilters.PolicyModeAccept); err != nil {
2441-
return nil, err
2451+
return nil, false, err
24422452
}
24432453
} else {
24442454
if err := fpb.add(eventType, report.Mode); err != nil {
2445-
return nil, err
2455+
return nil, false, err
24462456
}
24472457
}
24482458
}
24492459
if err := fpb.apply(p.Manager); err != nil {
2450-
return nil, fmt.Errorf("unable to apply to filter policy: %w", err)
2460+
return nil, false, fmt.Errorf("unable to apply to filter policy: %w", err)
24512461
}
24522462

24532463
eventTypes := rs.GetEventTypes()
@@ -2488,15 +2498,15 @@ func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, err
24882498
}
24892499

24902500
if err := p.updateProbes(eventTypes, needRawSyscalls); err != nil {
2491-
return nil, fmt.Errorf("failed to select probes: %w", err)
2501+
return nil, false, fmt.Errorf("failed to select probes: %w", err)
24922502
}
24932503

24942504
if p.opts.SyscallsMonitorEnabled {
24952505
if err := p.monitors.syscallsMonitor.Flush(); err != nil {
2496-
return nil, err
2506+
return nil, false, err
24972507
}
24982508
if err := p.monitors.syscallsMonitor.Enable(); err != nil {
2499-
return nil, err
2509+
return nil, false, err
25002510
}
25012511
}
25022512

@@ -2520,18 +2530,10 @@ func (p *EBPFProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, err
25202530
}
25212531

25222532
// do not replay the snapshot if we are in the first rule set version, this was already done in the start method
2523-
if p.ruleSetVersion != 0 {
2524-
p.replayEventsState.Store(true)
2525-
}
2526-
2533+
replayEvents := p.ruleSetVersion != 0
25272534
p.ruleSetVersion++
25282535

2529-
// trigger a nop event to ensure that the replayed events are processed
2530-
if err := p.triggerNopEvent(); err != nil {
2531-
seclog.Warnf("unable to trigger nop event: %v", err)
2532-
}
2533-
2534-
return filterReport, nil
2536+
return filterReport, replayEvents, nil
25352537
}
25362538

25372539
// OnNewRuleSetLoaded resets statistics and states once a new rule set is loaded

pkg/security/probe/probe_ebpfless.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,12 @@ func (p *EBPFLessProbe) FlushDiscarders() error {
636636
}
637637

638638
// ApplyRuleSet applies the new ruleset
639-
func (p *EBPFLessProbe) ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, error) {
640-
return &kfilters.FilterReport{}, nil
639+
func (p *EBPFLessProbe) ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, bool, error) {
640+
return &kfilters.FilterReport{}, false, nil
641+
}
642+
643+
// ReplayEvents replays the events from the rule set
644+
func (p *EBPFLessProbe) ReplayEvents() {
641645
}
642646

643647
// OnNewRuleSetLoaded resets statistics and states once a new rule set is loaded

pkg/security/probe/probe_others.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ func (p *Probe) NewRuleSet(_ map[eval.EventType]bool) *rules.RuleSet {
5353
}
5454

5555
// ApplyRuleSet setup the probes for the provided set of rules and returns the policy report.
56-
func (p *Probe) ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, error) {
57-
return nil, nil
56+
func (p *Probe) ApplyRuleSet(_ *rules.RuleSet) (*kfilters.FilterReport, bool, error) {
57+
return nil, false, nil
5858
}
5959

6060
// OnNewRuleSetLoaded resets statistics and states once a new rule set is loaded
@@ -85,6 +85,10 @@ func (p *Probe) IsNetworkEnabled() bool {
8585
return p.Config.Probe.NetworkEnabled
8686
}
8787

88+
// ReplayEvents replays the events from the rule set
89+
func (p *Probe) ReplayEvents() {
90+
}
91+
8892
// IsNetworkRawPacketEnabled returns whether network raw packet is enabled
8993
func (p *Probe) IsNetworkRawPacketEnabled() bool {
9094
return p.IsNetworkEnabled() && p.Config.Probe.NetworkRawPacketEnabled

pkg/security/probe/probe_windows.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,10 @@ func (p *WindowsProbe) setProcessContext(pid uint32, event *model.Event) error {
11691169
return err
11701170
}
11711171

1172+
// ReplayEvents replays the events from the rule set
1173+
func (p *WindowsProbe) ReplayEvents() {
1174+
}
1175+
11721176
// DispatchEvent sends an event to the probe event handler
11731177
func (p *WindowsProbe) DispatchEvent(event *model.Event) {
11741178
p.probe.logTraceEvent(event.GetEventType(), event)
@@ -1446,7 +1450,7 @@ func NewWindowsProbe(probe *Probe, config *config.Config, ipc ipc.Component, opt
14461450
}
14471451

14481452
// ApplyRuleSet setup the probes for the provided set of rules and returns the policy report.
1449-
func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, error) {
1453+
func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport, bool, error) {
14501454
p.enabledEventTypesLock.Lock()
14511455
clear(p.enabledEventTypes)
14521456
for _, eventType := range rs.GetEventTypes() {
@@ -1456,7 +1460,7 @@ func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport,
14561460

14571461
filterReport, err := kfilters.ComputeFilters(p.config.Probe, rs)
14581462
if err != nil {
1459-
return nil, err
1463+
return nil, false, err
14601464
}
14611465

14621466
// remove old approvers
@@ -1466,17 +1470,17 @@ func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.FilterReport,
14661470

14671471
for eventType, report := range filterReport.ApproverReports {
14681472
if err := p.setApprovers(eventType, report.Approvers); err != nil {
1469-
return nil, err
1473+
return nil, false, err
14701474
}
14711475
}
14721476

14731477
p.enabledEventTypesLock.RLock()
14741478
defer p.enabledEventTypesLock.RUnlock()
14751479
if err := p.reconfigureProvider(); err != nil {
1476-
return nil, err
1480+
return nil, false, err
14771481
}
14781482

1479-
return filterReport, nil
1483+
return filterReport, false, nil
14801484
}
14811485

14821486
// OnNewRuleSetLoaded resets statistics and states once a new rule set is loaded

pkg/security/rules/engine.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func (e *RuleEngine) LoadPolicies(providers []rules.PolicyProvider, sendLoadedRe
358358
ruleIDs = append(ruleIDs, events.AllCustomRuleIDs()...)
359359

360360
// analyze the ruleset, push probe evaluation rule sets to the kernel and generate the policy report
361-
filterReport, err := e.probe.ApplyRuleSet(rs)
361+
filterReport, replayEvents, err := e.probe.ApplyRuleSet(rs)
362362
if err != nil {
363363
return err
364364
}
@@ -371,6 +371,10 @@ func (e *RuleEngine) LoadPolicies(providers []rules.PolicyProvider, sendLoadedRe
371371

372372
e.currentRuleSet.Store(rs)
373373

374+
if replayEvents {
375+
e.probe.ReplayEvents()
376+
}
377+
374378
if err := e.probe.FlushDiscarders(); err != nil {
375379
return fmt.Errorf("failed to flush discarders: %w", err)
376380
}

0 commit comments

Comments
 (0)