SERVER-126758 Fix out-of-bounds read in $regexFindAll SBE builtin (#54441)

GitOrigin-RevId: d6e01eda89c1c12d0b62cff5d7c0182c2bf212d3
This commit is contained in:
Projjal Chanda 2026-05-26 15:56:31 +01:00 committed by MongoDB Bot
parent 9db5b85185
commit 100b7b1459
2 changed files with 47 additions and 0 deletions

View File

@ -218,4 +218,44 @@ TEST_F(SBERegexTest, ComputesRegexFindAll) {
runAndAssertFindAllExpression(compiledExpr.get(), arrayView);
}
TEST_F(SBERegexTest, RegexFindAllEmptyMatchOnEmptyInput) {
value::OwnedValueAccessor slotAccessor1;
value::OwnedValueAccessor slotAccessor2;
auto regexSlot = bindAccessor(&slotAccessor1);
auto inputSlot = bindAccessor(&slotAccessor2);
auto regexExpr = sbe::makeE<sbe::EFunction>(
EFn::kRegexFindAll, sbe::makeEs(makeE<EVariable>(regexSlot), makeE<EVariable>(inputSlot)));
auto compiledExpr = compileExpression(*regexExpr);
auto expectedArr = value::TagValueOwned::fromRaw(value::makeNewArray());
auto arrayView = value::getArrayView(expectedArr.value());
addMatchResult(arrayView, /*matchStr*/ "", /*idx*/ 0);
auto [regexTag, regexVal] = makeNewPcreRegex("a*", "");
auto [inputTag, inputVal] = value::makeNewString("");
slotAccessor1.reset(regexTag, regexVal);
slotAccessor2.reset(inputTag, inputVal);
runAndAssertFindAllExpression(compiledExpr.get(), arrayView);
}
TEST_F(SBERegexTest, RegexFindAllEndAnchorOnNonEmptyInput) {
value::OwnedValueAccessor slotAccessor1;
value::OwnedValueAccessor slotAccessor2;
auto regexSlot = bindAccessor(&slotAccessor1);
auto inputSlot = bindAccessor(&slotAccessor2);
auto regexExpr = sbe::makeE<sbe::EFunction>(
EFn::kRegexFindAll, sbe::makeEs(makeE<EVariable>(regexSlot), makeE<EVariable>(inputSlot)));
auto compiledExpr = compileExpression(*regexExpr);
auto expectedArr = value::TagValueOwned::fromRaw(value::makeNewArray());
auto arrayView = value::getArrayView(expectedArr.value());
addMatchResult(arrayView, /*matchStr*/ "", /*idx*/ 5);
auto [regexTag, regexVal] = makeNewPcreRegex("$", "");
auto [inputTag, inputVal] = value::makeNewString("hello");
slotAccessor1.reset(regexTag, regexVal);
slotAccessor2.reset(inputTag, inputVal);
runAndAssertFindAllExpression(compiledExpr.get(), arrayView);
}
} // namespace mongo::sbe

View File

@ -229,6 +229,13 @@ value::TagValueMaybeOwned ByteCode::builtinRegexFindAll(ArityType arity) {
auto [mstrTag, mstrVal] = value::getObjectView(matchVal)->getField("match");
auto matchString = value::getStringView(mstrTag, mstrVal);
if (matchString.empty()) {
// The regex matched an empty string. If the empty match landed at the end of the
// input (e.g. pattern "$" or "a*" against ""), 'startBytePos' is already at
// 'inputString.size()' and there is no byte to advance over. Break out so we do not
// read past the end of the input.
if (startBytePos >= inputString.size()) {
break;
}
startBytePos += str::getCodePointLength(inputString[startBytePos]);
++codePointPos;
} else {