snort: 5-MOAD scan COMPLETE; mark [x] SCAN-TODO; add TICKET.md snort3-0001

snort3-0001 MOAD-0001 CWE-407: ServiceDiscovery service_candidates std::find
dedup O(M*C) per packet in AppID — 72x at M=10000 (already committed)
snort3-0002 MOAD-0001 CWE-407: CHP match_tally std::find_if O(M*T) per HTTP
packet — 48x at T=100 (already committed)
MOAD-0002/0003/0004/0005: CLEAN
This commit is contained in:
russell@unturf.com 2026-04-03 13:23:47 -04:00
parent 0f2062b0cc
commit 1fb374fbc5
2 changed files with 61 additions and 1 deletions

View file

@ -0,0 +1,60 @@
# snort3-0001: CWE-407 — ServiceDiscovery service_candidates std::find dedup O(M*C)
## Target
Snort 3 network IDS/IPS: `src/network_inspectors/appid/service_plugins/service_discovery.cc`
## Defect
`match_by_pattern()` and `get_port_based_services()` both use `std::find` to dedup
`asd.service_candidates` (a `std::vector<ServiceDetector*>`). These functions are called
from `get_next_service()`, invoked once per packet during the AppID service discovery
phase while `asd.service_disco_state != APPID_DISCO_STATE_FINISHED`.
In `match_by_pattern()`: for each of M pattern match callbacks returned by the
Aho-Corasick engine, `std::find` walks all C existing candidates to check for
duplicates. Total work is O(M * C) per packet.
In `get_port_based_services()` (two-port midstream variant): for each detector in
port2's list, `std::find` walks port1's candidates. Total work is O(P1 * P2) where P1,
P2 are detector counts for each port.
With enterprise OpenAppID deployments shipping ~2900 Lua detectors, ports with many
registered detectors and packets matching many patterns produce measurable overhead on
every AppID-inspected packet.
## Fix
Seed an `std::unordered_set<ServiceDetector*> seen` from the existing candidates before
the loop, then use `seen.insert().second` for O(1) amortized dedup. Replaces both
`std::find` call sites.
## Complexity
O(M*C) -> O(M + C) per packet in match_by_pattern; O(P1*P2) -> O(P1 + P2) in
get_port_based_services. Measured: 72x op-count reduction at M=10000.
## Severity
HIGH. AppID service discovery runs on every packet of every flow until the service is
identified. In high-traffic environments (>10 Gbps), per-packet overhead compounds across
millions of concurrent flows.
## Patch
`defects/snort3-0001/patch/snort3-0001.patch`
## Test
`defects/snort3-0001/test/Snort3ServiceCandidateDedupTest.java` — Java simulation.
N=10000: defective 1069.7ms, fixed 14.8ms, ratio 72x. ALL PASS.
## All 5 MOAD Results for Snort 3
| MOAD | Status | Notes |
|------|--------|-------|
| 0001 (CWE-407) | DEFECT x2 | snort3-0001 (service_candidates, this); snort3-0002 (CHP match_tally) |
| 0002 (Intertangle) | CLEAN | SnortConfig immutable at runtime; per-thread flow state; no god object |
| 0003 (Leaked Context) | CLEAN | THREAD_LOCAL used correctly for per-thread packet stats; cleared per-packet |
| 0004 (CWE-312) | CLEAN | No sensitive headers logged verbatim; HTTP extractor omits Authorization field |
| 0005 (Thundering Herd) | CLEAN | LruCacheShared uses std::mutex lock_guard; LruCacheLocal is per-thread |