Skip to content

Commit 3b8bb5c

Browse files
committed
fix(rule-engine): Check process state before evaluation
Events can arrive in non-deterministic order since they are published by independent providers. The side effect of this can translate in form of a missing process state. Imagine OpenProcess event arriving from the process before the process' own creation event. To mitigate this bad behaviour, we always check the process state before evaluating out of order events.
1 parent a94d08a commit 3b8bb5c

File tree

3 files changed

+22
-13
lines changed

3 files changed

+22
-13
lines changed

pkg/rules/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func (e *Engine) Compile() (*config.RulesCompileResult, error) {
210210
for c, f := range filters {
211211
var ss *sequenceState
212212
if f.IsSequence() {
213-
ss = newSequenceState(f, c)
213+
ss = newSequenceState(f, c, e.psnap)
214214
}
215215
fltr := newCompiledFilter(f, c, ss)
216216
if ss != nil {

pkg/rules/sequence.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/rabbitstack/fibratus/pkg/kevent"
2929
"github.com/rabbitstack/fibratus/pkg/kevent/kparams"
3030
"github.com/rabbitstack/fibratus/pkg/kevent/ktypes"
31+
"github.com/rabbitstack/fibratus/pkg/ps"
3132
"github.com/rabbitstack/fibratus/pkg/util/atomic"
3233
log "github.com/sirupsen/logrus"
3334
"sort"
@@ -114,9 +115,11 @@ type sequenceState struct {
114115
states map[fsm.State]bool
115116
// smu guards the states map
116117
smu sync.RWMutex
118+
119+
psnap ps.Snapshotter
117120
}
118121

119-
func newSequenceState(f filter.Filter, c *config.FilterConfig) *sequenceState {
122+
func newSequenceState(f filter.Filter, c *config.FilterConfig, psnap ps.Snapshotter) *sequenceState {
120123
ss := &sequenceState{
121124
filter: f,
122125
seq: f.GetSequence(),
@@ -129,6 +132,7 @@ func newSequenceState(f filter.Filter, c *config.FilterConfig) *sequenceState {
129132
spanDeadlines: make(map[fsm.State]*time.Timer),
130133
initialState: sequenceInitialState,
131134
inDeadline: atomic.MakeBool(false),
135+
psnap: psnap,
132136
}
133137

134138
ss.initFSM()
@@ -480,6 +484,10 @@ func (s *sequenceState) runSequence(e *kevent.Kevent) bool {
480484
if !evt.ContainsMeta(kevent.RuleSequenceOOOKey) {
481485
continue
482486
}
487+
// try to initialize process state before evaluating the event
488+
if evt.PS == nil {
489+
_, evt.PS = s.psnap.Find(evt.PID)
490+
}
483491
matches = s.filter.RunSequence(evt, seqID, s.partials, false)
484492
// transition the state machine
485493
if matches {

pkg/rules/sequence_test.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/rabbitstack/fibratus/pkg/kevent"
2626
"github.com/rabbitstack/fibratus/pkg/kevent/kparams"
2727
"github.com/rabbitstack/fibratus/pkg/kevent/ktypes"
28+
"github.com/rabbitstack/fibratus/pkg/ps"
2829
pstypes "github.com/rabbitstack/fibratus/pkg/ps/types"
2930
log "github.com/sirupsen/logrus"
3031
"github.com/stretchr/testify/assert"
@@ -50,7 +51,7 @@ func TestSequenceState(t *testing.T) {
5051

5152
require.NoError(t, f.Compile())
5253

53-
ss := newSequenceState(f, c)
54+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
5455

5556
assert.Equal(t, 0, ss.currentState())
5657
assert.True(t, ss.isInitialState())
@@ -190,7 +191,7 @@ func TestSimpleSequence(t *testing.T) {
190191
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
191192
require.NoError(t, f.Compile())
192193

193-
ss := newSequenceState(f, c)
194+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
194195

195196
var tests = []struct {
196197
evts []*kevent.Kevent
@@ -276,7 +277,7 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) {
276277
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
277278
require.NoError(t, f.Compile())
278279

279-
ss := newSequenceState(f, c)
280+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
280281

281282
// create random matches which don't satisfy the sequence link
282283
for i, pid := range []uint32{2343, 1024, 11122, 3450, 12319} {
@@ -382,7 +383,7 @@ func TestSimpleSequenceDeadline(t *testing.T) {
382383
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
383384
require.NoError(t, f.Compile())
384385

385-
ss := newSequenceState(f, c)
386+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
386387

387388
e1 := &kevent.Kevent{
388389
Type: ktypes.CreateProcess,
@@ -453,7 +454,7 @@ func TestComplexSequence(t *testing.T) {
453454
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
454455
require.NoError(t, f.Compile())
455456

456-
ss := newSequenceState(f, c)
457+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
457458

458459
e1 := &kevent.Kevent{
459460
Seq: 1,
@@ -546,7 +547,7 @@ func TestSequenceOOO(t *testing.T) {
546547
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
547548
require.NoError(t, f.Compile())
548549

549-
ss := newSequenceState(f, c)
550+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
550551

551552
e1 := &kevent.Kevent{
552553
Type: ktypes.CreateFile,
@@ -606,7 +607,7 @@ func TestSequenceGC(t *testing.T) {
606607
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
607608
require.NoError(t, f.Compile())
608609

609-
ss := newSequenceState(f, c)
610+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
610611

611612
e := &kevent.Kevent{
612613
Type: ktypes.OpenProcess,
@@ -755,7 +756,7 @@ func TestSequenceExpire(t *testing.T) {
755756
f := filter.New(tt.expr, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
756757
require.NoError(t, f.Compile())
757758

758-
ss := newSequenceState(f, tt.c)
759+
ss := newSequenceState(f, tt.c, new(ps.SnapshotterMock))
759760
for _, evt := range tt.evts {
760761
if evt.IsTerminateProcess() {
761762
ss.expire(evt)
@@ -787,7 +788,7 @@ func TestSequenceBoundFields(t *testing.T) {
787788
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
788789
require.NoError(t, f.Compile())
789790

790-
ss := newSequenceState(f, c)
791+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
791792

792793
e1 := &kevent.Kevent{
793794
Type: ktypes.CreateProcess,
@@ -882,7 +883,7 @@ func TestSequenceBoundFieldsWithFunctions(t *testing.T) {
882883
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true, EnableRegistryKevents: true}, Filters: &config.Filters{}})
883884
require.NoError(t, f.Compile())
884885

885-
ss := newSequenceState(f, c)
886+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
886887

887888
e1 := &kevent.Kevent{
888889
Type: ktypes.CreateFile,
@@ -942,7 +943,7 @@ func TestIsExpressionEvaluable(t *testing.T) {
942943
`, &config.Config{Kstream: config.KstreamConfig{EnableFileIOKevents: true}, Filters: &config.Filters{}})
943944
require.NoError(t, f.Compile())
944945

945-
ss := newSequenceState(f, c)
946+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
946947

947948
e1 := &kevent.Kevent{
948949
Type: ktypes.CreateProcess,

0 commit comments

Comments
 (0)