From d70316f894e7c0762ff070b109538ec893371e84 Mon Sep 17 00:00:00 2001 From: 0z5a Date: Fri, 13 Nov 2026 09:01:27 +0100 Subject: [PATCH] Reject \x without hex digits in cUnescape cUnescape treated "\x" followed by a non-hex character as an escape with no digits and appended a NUL byte: "a\xg" became "a\0g" in both strict and non-strict mode. The documented grammar is \x[0-9a-fA-F]+, and "\x" at the end of the input already throws "incomplete hex escape sequence" in strict mode and is kept as "\x" in non-strict mode. Handle "\x" followed by any non-hex character the same way as "\x" at the end of the input. --- folly/String-inl.h | 4 +++- folly/test/StringTest.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/folly/String-inl.h b/folly/String-inl.h index b5021d8679d..caa3cc5b71e 100644 --- a/folly/String-inl.h +++ b/folly/String-inl.h @@ -118,7 +118,9 @@ void cUnescape(StringPiece str, String& out, bool strict) { last = p; } else if (e == 'X') { // hex ++p; - if (p == str.end()) { // \x at end of string + if (p == str.end() || + detail::hexTable[static_cast(*p)] >= 16) { + // \x not followed by a hex digit if (strict) { throw_exception( "incomplete hex escape sequence"); diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index cdbf8d36f3b..993314cb6de 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -378,6 +378,18 @@ TEST(Escape, cUnescape) { cUnescape("hello\\q"), std::invalid_argument, "invalid escape sequence"); + + // \x must be followed by at least one hex digit, also before other text. + EXPECT_THROW_RE( + cUnescape("hello\\xg"), + std::invalid_argument, + "incomplete hex escape sequence"); + EXPECT_THROW_RE( + cUnescape("\\x\\n"), + std::invalid_argument, + "incomplete hex escape sequence"); + EXPECT_EQ("hello\\xg", cUnescape("hello\\xg", false)); + EXPECT_EQ("\\x\n", cUnescape("\\x\\n", false)); } TEST(Escape, uriEscape) {