SERVER-86326 Increase regex max pattern length to 32764 characters (#44702)
GitOrigin-RevId: e9c1477062541d99b450688b036be72a5ccc87c1
This commit is contained in:
parent
30e5202326
commit
48f10211e3
@ -496,6 +496,8 @@ last-continuous:
|
||||
ticket: SERVER-113887
|
||||
- test_file: jstests/sharding/disable_resumable_range_deleter.js
|
||||
ticket: SERVER-112357
|
||||
- test_file: jstests/core/query/regex/regex_max_pattern_length.js
|
||||
ticket: SERVER-86326
|
||||
suites: null
|
||||
last-lts:
|
||||
all:
|
||||
@ -1049,4 +1051,6 @@ last-lts:
|
||||
ticket: SERVER-113887
|
||||
- test_file: jstests/sharding/disable_resumable_range_deleter.js
|
||||
ticket: SERVER-112357
|
||||
- test_file: jstests/core/query/regex/regex_max_pattern_length.js
|
||||
ticket: SERVER-86326
|
||||
suites: null
|
||||
|
||||
@ -41,16 +41,19 @@ function testRegexAggException(inputObj, exceptionCode, expression) {
|
||||
}
|
||||
|
||||
(function testLongRegex() {
|
||||
// Our limit on regex pattern length is 2^14.
|
||||
// The maximum allowed pattern length is 16384 before SERVER-86326, and 32764 with it.
|
||||
// Using a limit of 16384 should work across all versions in multiversion tests.
|
||||
const kMaxRegexPatternLen = 16384;
|
||||
const patternMaxLen = "c".repeat(kMaxRegexPatternLen);
|
||||
const patternMaxLen = "c".repeat(kMaxRegexPatternLen - 1);
|
||||
const anchoredPatternMaxLen = "^" + patternMaxLen;
|
||||
|
||||
// Test that a regex with maximum allowable pattern length can find a document.
|
||||
testRegexAgg({input: "$z", regex: patternMaxLen},
|
||||
testRegexAgg({input: "$z", regex: anchoredPatternMaxLen},
|
||||
[{match: patternMaxLen, "idx": 0, "captures": []}]);
|
||||
|
||||
// Test that a regex pattern exceeding the limit fails.
|
||||
const patternTooLong = patternMaxLen + "c";
|
||||
// Test that a regex pattern exceeding the limit fails. A pattern length of 32765 is
|
||||
// disallowed in all versions in multiversion tests.
|
||||
const patternTooLong = "c".repeat(32765);
|
||||
testRegexAggException({input: "$z", regex: patternTooLong}, 51111);
|
||||
})();
|
||||
|
||||
|
||||
33
jstests/core/query/regex/regex_max_pattern_length.js
Normal file
33
jstests/core/query/regex/regex_max_pattern_length.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @tags: [
|
||||
* assumes_read_concern_local,
|
||||
* # Retryable writes are required for the remove() command in the test.
|
||||
* requires_non_retryable_writes,
|
||||
* ]
|
||||
*/
|
||||
const kCollName = jsTestName();
|
||||
const kMaxOldRegexPatternLength = 16384;
|
||||
|
||||
const buildAnchoredPattern = (length) => "^" +
|
||||
"y".repeat(length - 1);
|
||||
|
||||
const coll = db[kCollName];
|
||||
coll.drop();
|
||||
|
||||
// Assume that the following queries succeed, despite long patterns being used.
|
||||
[0, 1, 100, 1000, 10000].forEach((length) => {
|
||||
coll.remove({});
|
||||
assert.commandWorked(coll.insert({a: "y".repeat(kMaxOldRegexPatternLength + length)}));
|
||||
assert.eq(1,
|
||||
coll.find({a: {$regex: buildAnchoredPattern(kMaxOldRegexPatternLength + length)}})
|
||||
.itcount());
|
||||
});
|
||||
|
||||
// Using a too long pattern will always fail.
|
||||
let error = assert.throws(() => coll.find({a: {$regex: buildAnchoredPattern(32767)}}).itcount());
|
||||
assert.commandFailedWithCode(error, 51091);
|
||||
assert(
|
||||
error.message.includes(
|
||||
"Regular expression is invalid: pattern string is longer than the limit set by the application",
|
||||
),
|
||||
);
|
||||
@ -125,8 +125,9 @@ const std::error_category& pcreCategory() noexcept {
|
||||
namespace detail {
|
||||
|
||||
|
||||
// Global.
|
||||
inline constexpr size_t kMaxPatternLength = 16384;
|
||||
// Maximum length of the pattern accepted by PCRE2, in bytes. The effectively usable pattern length
|
||||
// may be less than this value.
|
||||
constexpr size_t kMaxPatternLength = 32764;
|
||||
|
||||
/** Wrapper around a pcre2_compile_context. */
|
||||
class CompileContext {
|
||||
|
||||
@ -443,5 +443,32 @@ TEST(PcreTest, HeapLimit) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PcreTest, MaxPatternLength) {
|
||||
const std::string s(1024, 'y');
|
||||
|
||||
// Test patterns of various lengths
|
||||
for (uint32_t patternLength : {2, 5, 10, 100, 1000, 10000, 16384, 20000, 30000, 32764}) {
|
||||
std::string pattern = fmt::format("^{}", std::string(patternLength - 1, 'x'));
|
||||
|
||||
// We use an anchored pattern here in order to reduce internal memory usage during regex
|
||||
// execution.
|
||||
Regex customLimitRegex(pattern, CompileOptions{});
|
||||
ASSERT_FALSE(customLimitRegex.error());
|
||||
auto match = customLimitRegex.match(s);
|
||||
ASSERT_TRUE(match.error());
|
||||
ASSERT_EQ(match.error().message(), "no match");
|
||||
}
|
||||
|
||||
// Test a pattern that is above the maximum allowed pattern length.
|
||||
{
|
||||
const std::string kTooLongErrorMessage =
|
||||
"pattern string is longer than the limit set by the application";
|
||||
const std::string pattern = fmt::format("^{}", std::string(32764, 'x'));
|
||||
Regex defaultLimitRegex(pattern, CompileOptions{});
|
||||
ASSERT_TRUE(defaultLimitRegex.error());
|
||||
ASSERT_EQ(defaultLimitRegex.error().message(), kTooLongErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo::pcre
|
||||
|
||||
Loading…
Reference in New Issue
Block a user