Skip to content

uhv: use-after-free read in sanitizeHeadersWithUnderscores with duplicate underscore headers #44032

@1seal

Description

@1seal

summary

HeaderValidator::sanitizeHeadersWithUnderscores() stores header names as non-owning absl::string_view references and then removes headers from the header map while iterating. if the request contains duplicate header names with underscores, later iterations construct a LowerCaseString from a dangling view, producing a use-after-free read.

callsite: source/extensions/http/header_validators/envoy_default/header_validator.cc:631-652

prerequisites

  • UHV enabled in build/runtime (envoy.reloadable_features.enable_universal_header_validator)
  • headers_with_underscores_action: DROP_HEADER configured in HTTP protocol options

root cause

the function collects header names with underscores into std::vector<absl::string_view>. it then iterates and calls header_map.remove(LowerCaseString(name)) for each. when two entries refer to the same backing storage (duplicate header names), the first removal frees the storage and the second iteration reads from a dangling view.

reproduction

send a request with duplicate header names containing underscores (e.g. x_foo twice) through an envoy instance with UHV enabled and DROP_HEADER configured. under ASAN the heap-use-after-free read is deterministic.

suggested fix

change the collected names from absl::string_view to std::string (owning copies) so that removals don't invalidate remaining references:

-  std::vector<absl::string_view> drop_headers;
+  std::vector<std::string> drop_headers;
   header_map.iterate([&drop_headers](const ::Envoy::Http::HeaderEntry& header_entry)
                          -> ::Envoy::Http::HeaderMap::Iterate {
     const absl::string_view header_name = header_entry.key().getStringView();
     if (absl::StrContains(header_name, '_')) {
-      drop_headers.push_back(header_name);
+      drop_headers.emplace_back(header_name);
     }

     return ::Envoy::Http::HeaderMap::Iterate::Continue;
   });

alternatively, deduplicating names before iterating and mutating the map would also prevent the dangling reference.

happy to open a PR with the fix + a regression test if that's preferred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    stalestalebot believes this issue/PR has not been touched recently

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions