java-topology/defects/envoy/patch/envoy-0004-http2-settings-dedup-linear-scan.md
russell@unturf.com 25c2bafdee undf: assign 694-720; stamp patches; ruby-0003/elixir-0002/r-source-0002/victoria-metrics-0002
New UNDF assignments (693→720):
  elixir-0002 → UNDF-2026-000000698 (typespec used_type_pairs O(T²))
  r-source-0002 → UNDF-2026-000000711 (.walkClassGraph match dedup O(S²))
  ruby-0003 → UNDF-2026-000000712 (RubyGems dependent_gems O(N²×D))
  victoria-metrics-0002 → UNDF-2026-000000717 (MetricName tag-filter O(T×I))

Total: 720 UNDF assigned
2026-03-29 22:28:31 -04:00

3.2 KiB
Raw Blame History

UNDF: UNDF-2026-000000699

envoy-0004: CWE-407 — O(S²) linear dedup in HTTP/2 sendSettingsHelper per connection

Severity: MEDIUM

Repository

github.com/envoyproxy/envoy Commit: HEAD (main branch)

File

source/common/http/http2/codec_impl.cc

Defective Lines

// Line 1733-1744
void ConnectionImpl::sendSettingsHelper(...) {
  absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
  auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool {
    // Consider using a set as an intermediate data structure, rather than this ad-hoc
    // deduplication.                                              ^^^ developer TODO
    const auto it = std::find_if(
        settings.cbegin(), settings.cend(),
        [&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; });
    if (it != settings.end()) {
      return false;
    }
    settings.push_back(entry);
    return true;
  };
  ...
  for (const auto& it : http2_options.custom_settings_parameters()) {  // outer: O(S)
    insertParameter({...});                                              // inner: O(S) scan
  }

Complexity

O(S²) where S = number of custom_settings_parameters entries configured in the HTTP/2 protocol options. Called once per HTTP/2 connection establishment, on both client and server sides.

The developer left a comment explicitly acknowledging the issue:

// Consider using a set as an intermediate data structure, rather than this
// ad-hoc deduplication.

This is triggered at connection setup for every H2 connection: downstream (listener accept path) and upstream (cluster connect path).

At S=50 custom settings, each connection setup performs ~1,250 comparisons in std::find_if — 50× the O(S) cost with a hash map.

Impact

  • Per-connection cost on H2 connection establishment
  • In high-connection-churn deployments (connection pooling disabled, or short connections) this executes O(C × S²) where C is connection rate
  • In default configs S is small (0-5), but enterprise deployments with custom H2 tuning parameters (flow control, stream limits, etc.) can have large S

Fix

Replace the InlinedVector + std::find_if dedup with absl::flat_hash_map for O(1) per-insertion duplicate detection:

void ConnectionImpl::sendSettingsHelper(
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
  // Use flat_hash_map for O(1) duplicate detection instead of O(S) std::find_if.
  absl::flat_hash_map<http2::adapter::Http2SettingsId, uint32_t> settings_map;

  auto insertParameter = [&settings_map](const http2::adapter::Http2Setting& entry) -> bool {
    return settings_map.emplace(entry.id, entry.value).second;
  };
  ...
  // Convert map to vector for SubmitSettings
  absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
  settings.reserve(settings_map.size());
  for (const auto& [id, value] : settings_map) {
    settings.push_back({id, value});
  }
  adapter_->SubmitSettings(settings);
}

Benchmark

  • S=50 custom settings: 1,250 comparisons → 50 hash insertions (~25x)
  • S=100 custom settings: 5,000 comparisons → 100 hash insertions (~50x)
  • Complexity: O(S²) → O(S)

Unit Test

See defects/envoy/unit/Envoy0004Test.java