Skip to content

Commit 4f97333

Browse files
authored
Fix use-after-free in ParsedConfigCache for short config strings (#13078)
- Fix use-after-free in ParsedConfigCache when config values are short enough for std::string SSO (Small String Optimization) - ParsedValue::parse() returned by value, and emplace moved it into the map — relocating the SSO inline buffer while string_views in TargetedCacheControlHeaders::headers[] still pointed to the old address - Make ParsedValue non-movable and use try_emplace + parse_into() so parsing happens directly in the map node - Also fixes the same class of bug for HostResData::conf_value and HttpStatusCodeList::conf_value pointers Reproducer: configure conf_remap with a short targeted header value like ACME-Cache-Control (18 chars, within libc++ SSO threshold of 22). The string_views in the per-transaction override become dangling, causing incorrect cache behavior. The SSO threshold varies by standard library — libc++ (macOS/clang): 22 bytes, libstdc++ (GCC/Linux): 15 bytes. A value like ACME-Cache-Control (18 chars) triggers SSO on libc++ but uses heap allocation on libstdc++, where the buffer pointer survives the move. This is why the bug may reproduce on macOS but not on Linux CI with GCC.
1 parent c52cddf commit 4f97333

2 files changed

Lines changed: 11 additions & 8 deletions

File tree

include/proxy/http/HttpConfig.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,12 @@ class ParsedConfigCache
933933
std::variant<std::monostate, HostResData, HttpStatusCodeList, HttpForwarded::OptionBitSet, MgmtByte,
934934
TargetedCacheControlHeaders>
935935
parsed{};
936+
937+
ParsedValue() = default;
938+
ParsedValue(const ParsedValue &) = delete;
939+
ParsedValue &operator=(const ParsedValue &) = delete;
940+
ParsedValue(ParsedValue &&) = delete;
941+
ParsedValue &operator=(ParsedValue &&) = delete;
936942
};
937943

938944
/** Return the parsed value for the configuration.
@@ -958,7 +964,7 @@ class ParsedConfigCache
958964
static ParsedConfigCache &instance();
959965

960966
const ParsedValue &lookup_impl(TSOverridableConfigKey key, std::string_view value);
961-
ParsedValue parse(TSOverridableConfigKey key, std::string_view value);
967+
void parse_into(ParsedValue &result, TSOverridableConfigKey key, std::string_view value);
962968

963969
// Custom hash for the cache key.
964970
struct CacheKeyHash {

src/proxy/http/HttpConfig.cc

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -786,15 +786,14 @@ ParsedConfigCache::lookup_impl(TSOverridableConfigKey key, std::string_view valu
786786
}
787787

788788
// Parse and insert.
789-
auto [inserted_it, success] = _cache.emplace(cache_key, parse(key, value));
789+
auto [inserted_it, success] = _cache.try_emplace(cache_key);
790+
parse_into(inserted_it->second, key, value);
790791
return inserted_it->second;
791792
}
792793

793-
ParsedConfigCache::ParsedValue
794-
ParsedConfigCache::parse(TSOverridableConfigKey key, std::string_view value)
794+
void
795+
ParsedConfigCache::parse_into(ParsedValue &result, TSOverridableConfigKey key, std::string_view value)
795796
{
796-
ParsedValue result{};
797-
798797
// Store the string value - the parsed structures may reference this.
799798
result.conf_value_storage = std::string(value);
800799

@@ -843,8 +842,6 @@ ParsedConfigCache::parse(TSOverridableConfigKey key, std::string_view value)
843842
// No special parsing needed for this config.
844843
break;
845844
}
846-
847-
return result;
848845
}
849846

850847
/** Template for creating conversions and initialization for @c std::chrono based configuration variables.

0 commit comments

Comments
 (0)