java-topology/defects/envoy/patch/envoy-0002-ext-proc-namespace-linear-scan.md

2.7 KiB
Raw Blame History

UNDF: UNDF-2026-000000390

envoy-0001: CWE-407 — Linear namespace membership scan on every ext_proc response, per-request

Severity: HIGH

Repository

github.com/envoyproxy/envoy Commit: a2fe7fb

File

source/extensions/filters/http/ext_proc/ext_proc.cc

Defective Lines

1636:   auto receiving_namespaces = state.untypedReceivingMetadataNamespaces();
1637:   for (const auto& context_key : response_metadata) {        // outer: O(M) metadata keys
1638:       bool found_allowed_namespace = false;
1639:       if (auto metadata_it =
1640:               std::find(receiving_namespaces.begin(),          // inner: O(N) linear scan
1641:                         receiving_namespaces.end(),
1642:                         context_key.first);
1643:           metadata_it != receiving_namespaces.end()) {

Type of receiving_namespaces

std::vector<std::string> — declared at ext_proc.h:396 and ext_proc.h:686. untypedReceivingMetadataNamespaces() returns a const std::vector<std::string>&.

Complexity

O(M × N) per HTTP request that triggers ext_proc dynamic metadata processing, where:

  • M = number of metadata keys in the ext_proc gRPC response
  • N = number of configured receiving namespaces

This executes on the request data plane hot path inside handleDynamicMetadata(), called from processResponse() for every ext_proc filter response.

Impact

At M=10 metadata keys and N=50 configured receiving namespaces, each ext_proc response triggers 500 string comparisons. At 10,000 RPS this is 5,000,000 string comparisons per second in a single worker thread. Latency spikes scale with N. The receiving_namespaces vector is rebuilt from config on every call via state.untypedReceivingMetadataNamespaces() (defensive copy at line 1636).

Fix

Replace std::vector<std::string> with absl::flat_hash_set<std::string> for the receiving namespaces collection. Build once at filter config parse time; O(1) amortized lookup per metadata key.

// In ExternalProcessorConfig (ext_proc.h):
// Before:
const std::vector<std::string> untyped_receiving_namespaces_;

// After:
const absl::flat_hash_set<std::string> untyped_receiving_namespaces_;

// In handleDynamicMetadata (ext_proc.cc):
// Before:
if (auto metadata_it =
        std::find(receiving_namespaces.begin(), receiving_namespaces.end(), context_key.first);
    metadata_it != receiving_namespaces.end()) {

// After:
if (receiving_namespaces.contains(context_key.first)) {

References

  • CWE-407: Inefficient Algorithmic Complexity
  • source/extensions/filters/http/ext_proc/ext_proc.cc handleDynamicMetadata() line 1636-1643
  • source/extensions/filters/http/ext_proc/ext_proc.h line 303-304, 396, 686