From bc1d8ce5ae65b3e8622ad306a2cabf731a42af3f Mon Sep 17 00:00:00 2001 From: robobun Date: Fri, 28 Aug 2026 14:07:22 +0000 Subject: [PATCH] URLParser: stop crashing on URL-encoded forms that do not fit a Vector parseURLEncodedForm appended every pair with Vector::append, which CRASH()es when a 1.5x growth step asks for a capacity over isValidCapacityForVector. With 16-byte pairs that is the 116597314th pair. formURLDecode called StringView::utf8(), which RELEASE_ASSERTs when the UTF-8 form of a name or value does not fit a Vector, and then String::fromUTF8ReplacingInvalidSequences, which crashes on more than 2^30 - 1 non-ASCII bytes. tryParseURLEncodedForm(input, maxPairs) appends with tryAppend and returns nullopt past maxPairs or once the Vector cannot grow. parseURLEncodedForm keeps its signature and returns an empty form in that case. formURLDecode uses tryGetUTF8 and checks the char16_t Vector capacity. It returns nullopt for a value it cannot decode, which parseQueryNameAndValue already treats as a pair to skip. --- Source/WTF/wtf/URLParser.cpp | 23 +++++++++++++++++------ Source/WTF/wtf/URLParser.h | 3 +++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Source/WTF/wtf/URLParser.cpp b/Source/WTF/wtf/URLParser.cpp index 70476811098b..7c1fea25d7a7 100644 --- a/Source/WTF/wtf/URLParser.cpp +++ b/Source/WTF/wtf/URLParser.cpp @@ -3887,24 +3887,35 @@ auto URLParser::parseHostAndPort(CodePointIterator iterator) -> H std::optional URLParser::formURLDecode(StringView input) { - auto utf8 = input.utf8(StrictConversion); - if (utf8.isNull()) + auto utf8 = input.tryGetUTF8(StrictConversion); + if (!utf8) + return std::nullopt; + auto percentDecoded = percentDecode(byteCast(utf8->span())); + // fromUTF8ReplacingInvalidSequences sizes a Vector by the byte count unless the bytes are all ASCII. + if (!isValidCapacityForVector(percentDecoded.size()) && !charactersAreAllASCII(percentDecoded.span())) return std::nullopt; - auto percentDecoded = percentDecode(byteCast(utf8.span())); return String::fromUTF8ReplacingInvalidSequences(percentDecoded.span()); } // https://url.spec.whatwg.org/#concept-urlencoded-parser -auto URLParser::parseURLEncodedForm(StringView input) -> URLEncodedForm +auto URLParser::tryParseURLEncodedForm(StringView input, size_t maxPairs) -> std::optional { URLEncodedForm output; for (StringView bytes : input.split('&')) { - if (auto nameAndValue = parseQueryNameAndValue(bytes)) - output.append(WTF::move(*nameAndValue)); + auto nameAndValue = parseQueryNameAndValue(bytes); + if (!nameAndValue) + continue; + if (output.size() >= maxPairs || !output.tryAppend(WTF::move(*nameAndValue))) + return std::nullopt; } return output; } +auto URLParser::parseURLEncodedForm(StringView input) -> URLEncodedForm +{ + return tryParseURLEncodedForm(input).value_or(URLEncodedForm { }); +} + std::optional> URLParser::parseQueryNameAndValue(StringView bytes) { auto equalIndex = bytes.find('='); diff --git a/Source/WTF/wtf/URLParser.h b/Source/WTF/wtf/URLParser.h index 95c3361e6822..86a5215a84ad 100644 --- a/Source/WTF/wtf/URLParser.h +++ b/Source/WTF/wtf/URLParser.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include #include @@ -56,6 +57,8 @@ class URLParser { using URLEncodedForm = Vector>; WTF_EXPORT_PRIVATE static URLEncodedForm parseURLEncodedForm(StringView); + // Returns nullopt when the form has more than maxPairs pairs, or more than the Vector can grow to hold. + WTF_EXPORT_PRIVATE static std::optional tryParseURLEncodedForm(StringView, size_t maxPairs = std::numeric_limits::max()); WTF_EXPORT_PRIVATE static std::optional> parseQueryNameAndValue(StringView); WTF_EXPORT_PRIVATE static String serialize(const URLEncodedForm&);