fix: eliminate stale verdict poisoning, memory leaks, data races, and per-packet allocations in engine

This commit is contained in:
2026-05-15 02:08:22 +00:00
parent bc25169f41
commit 301c252c43
15 changed files with 222 additions and 163 deletions
+24 -3
View File
@@ -3,6 +3,7 @@ package engine
import (
"net"
"sync"
"time"
"git.difuse.io/Difuse/Mellaris/analyzer"
"git.difuse.io/Difuse/Mellaris/io"
@@ -13,6 +14,8 @@ import (
const tcpFlowMaxBuffer = 16384
const tcpFlowIdleTimeout = 10 * time.Minute
type tcpFlowDirection uint8
const (
@@ -37,6 +40,7 @@ type tcpFlow struct {
doneEntries []*tcpFlowEntry
lastVerdict io.Verdict
feedCalled [2]bool
lastSeen time.Time
}
type tcpFlowEntry struct {
@@ -67,16 +71,17 @@ func (f *tcpFlow) feed(l3 L3Info, tcp TCPInfo, payload []byte) io.Verdict {
expected := f.dirSeq[dir]
if !f.feedCalled[dir] || expected == 0 || tcp.Seq == expected {
f.feedCalled[dir] = true
f.dirBuf[dir] = append(f.dirBuf[dir], payload...)
f.dirSeq[dir] = tcp.Seq + uint32(len(payload))
if len(f.dirBuf[dir]) <= tcpFlowMaxBuffer {
f.dirBuf[dir] = append(f.dirBuf[dir], payload...)
propUpdated = f.feedAnalyzers(rev)
}
f.dirSeq[dir] = tcp.Seq + uint32(len(payload))
}
}
f.runMatch(rs, version, rulesetChanged, propUpdated)
f.maybeFinalizeVerdict()
f.lastSeen = time.Now()
return f.lastVerdict
}
@@ -218,7 +223,11 @@ func (m *tcpFlowManager) createFlow(streamID uint32, l3 L3Info, tcp TCPInfo, pay
Props: make(analyzer.CombinedPropMap),
}
m.logger.TCPStreamNew(m.workerID, info)
rs, version := m.rulesetSource()
var rs ruleset.Ruleset
var version uint64
if m.rulesetSource != nil {
rs, version = m.rulesetSource()
}
var ans []analyzer.TCPAnalyzer
if rs != nil {
baseAns := rs.Analyzers(info)
@@ -255,6 +264,7 @@ func (m *tcpFlowManager) createFlow(streamID uint32, l3 L3Info, tcp TCPInfo, pay
rulesetVersion: version,
activeEntries: entries,
lastVerdict: io.VerdictAccept,
lastSeen: time.Now(),
}
flow.dirSeq[tcpDirC2S] = tcp.Seq + 1
return flow
@@ -266,6 +276,17 @@ func (m *tcpFlowManager) updateRuleset(r ruleset.Ruleset, version uint64) {
}
}
func (m *tcpFlowManager) cleanupIdle(now time.Time) {
m.mu.Lock()
defer m.mu.Unlock()
for id, flow := range m.flows {
if now.Sub(flow.lastSeen) > tcpFlowIdleTimeout {
flow.closeActiveEntries()
delete(m.flows, id)
}
}
}
func feedFlowEntry(entry *tcpFlowEntry, rev bool, data []byte) (update *analyzer.PropUpdate, closeUpdate *analyzer.PropUpdate, done bool) {
if !entry.HasLimit {
update, done = entry.Stream.Feed(rev, true, false, 0, data)