Skip to content

PATTERN Cited by 1 source

Approver / discarder filter

The approver / discarder filter is a named dual used in Datadog's Workload Protection FIM to do correct in-kernel pre-filtering against an evolving rule engine, despite eBPF verifier limits preventing the full rule logic from running in-kernel.

It combines two complementary filter sets stored in eBPF maps:

  • Approversstatic, positive filters derived at rule compile time. "This specific value matches some rule's constant condition; forward it."
  • Discardersdynamic, negative filters learned at runtime by the rule engine. "This specific value will never match any active rule; drop it."

Together they approximate the full rule engine's decision boundary closely enough that ~94% of events are resolved in-kernel, with user-space handling only the ambiguous remainder.

How approvers are generated

At rule compile time, the rule engine inspects each predicate for concrete extractable values:

open.file.path == "/etc/passwd" && open.flags & O_CREAT > 0

/etc/passwd is a concrete constant → added to the open.file.path approver map. open.flags & O_CREAT > 0 is a bitmask predicate — whether it yields an approver depends on the engine's handling of flag fields.

When hundreds of rules each mention different fields and syscalls, "it quickly becomes a complex optimization problem" — the rule engine has to decide which fields to approve on (memory vs. coverage tradeoff).

Wildcards defeat approvers: open.file.path == "/etc/*" has no concrete string to extract. That's what discarders are for.

How discarders are generated

At runtime, when the rule engine observes a value it has not previously evaluated (e.g. a /tmp/foo file access under a ruleset targeting /etc/*), it reasons:

Given the current active ruleset, can any rule ever match a value whose open.file.path lies under /tmp?

If no → /tmp (as a prefix, or some engine-specific encoding) is loaded into a discarder LRU map. Future /tmp-prefixed events are dropped in-kernel by the eBPF program.

The map is LRU deliberately — the working set of discarder keys grows with observed traffic but must stay bounded; least-recently- used entries age out, so only hot noise sources stay resident.

Correctness invariants

  • Approvers must be a subset of "could match" — no false positives would be silently missed, but a false negative in an approver (a missed concrete value) just means that event follows the slow-path into user-space; no correctness issue.
  • Discarders must be a strict subset of "can never match" — a bad discarder silently drops a would-match event → security blind spot. This invariant is what the rule engine's "non-trivial algorithms" protect.
  • Rule changes invalidate prior discarders — the rule engine has to flush/rebuild the discarder map on ruleset change.

Generalization beyond FIM

The pattern is applicable whenever:

  • A high-volume event stream must be matched against a rule engine.
  • The full rule engine can't run at emit time (perf, safety, verifier limits).
  • Rules are compilable into a mix of static constants (feeding approvers) and wildcard / pattern clauses (feeding discarder-style dynamic learning).

Examples elsewhere: tcpdump-style BPF filters (compile-time only; the approver half), XDP/L4 firewalls, sidecar sampling decisions, observability collector filters.

Seen in

  • sources/2025-11-18-datadog-ebpf-fim-filtering — Datadog FIM's canonical use: approvers from compiled detection rules, discarders learned from runtime traffic, both stored in eBPF maps; ~94% of ~10B events/min resolved in-kernel.
Last updated · 200 distilled / 1,178 read