@@ -724,15 +724,24 @@ namespace Js
724
724
char16 currentChar = replaceStr[substitutionOffset + 1 ];
725
725
if (currentChar >= _u (' 0' ) && currentChar <= _u (' 9' ))
726
726
{
727
+ // We've found a substitution ref, like $32. In accordance with the standard (sec-getsubstitution),
728
+ // we recognize at most two decimal digits after the dollar sign.
729
+
730
+ // This should be unsigned, but this would cause lots of compiler warnings unless we also make
731
+ // numGroups unsigned, because of a comparison below.
727
732
int captureIndex = (int )(currentChar - _u (' 0' ));
733
+ Assert (0 <= captureIndex && captureIndex <= 9 ); // numeric value of single decimal digit
734
+
728
735
offset = substitutionOffset + 2 ;
729
736
730
737
if (offset < replaceLength)
731
738
{
732
739
currentChar = replaceStr[substitutionOffset + 2 ];
733
740
if (currentChar >= _u (' 0' ) && currentChar <= _u (' 9' ))
734
741
{
742
+ // Should also be unsigned; see captureIndex above.
735
743
int tempCaptureIndex = (10 * captureIndex) + (int )(currentChar - _u (' 0' ));
744
+ Assert (0 <= tempCaptureIndex && tempCaptureIndex < 100 ); // numeric value of 2-digit positive decimal number
736
745
if (tempCaptureIndex < numGroups)
737
746
{
738
747
captureIndex = tempCaptureIndex;
@@ -741,6 +750,7 @@ namespace Js
741
750
}
742
751
}
743
752
753
+ Assert (0 <= captureIndex && captureIndex < 100 ); // as above, value of 2-digit positive decimal number
744
754
if (captureIndex < numGroups && (captureIndex != 0 ))
745
755
{
746
756
Var group = getGroup (captureIndex, nonMatchValue);
@@ -1204,10 +1214,13 @@ namespace Js
1204
1214
JavascriptString* newString = nullptr ;
1205
1215
const char16* inputStr = input->GetString ();
1206
1216
CharCount inputLength = input->GetLength ();
1207
- const int numGroups = pattern->NumGroups ();
1217
+ const int rawNumGroups = pattern->NumGroups ();
1208
1218
Var nonMatchValue = NonMatchValue (scriptContext, false );
1209
1219
UnifiedRegex::GroupInfo lastMatch; // initially undefined
1210
1220
1221
+ AssertOrFailFast (0 < rawNumGroups && rawNumGroups <= INT16_MAX);
1222
+ const uint16 numGroups = uint16 (rawNumGroups);
1223
+
1211
1224
#if ENABLE_REGEX_CONFIG_OPTIONS
1212
1225
RegexHelperTrace (scriptContext, UnifiedRegex::RegexStats::Replace, regularExpression, input, scriptContext->GetLibrary ()->CreateStringFromCppLiteral (_u (" <replace function>" )));
1213
1226
#endif
@@ -1265,7 +1278,6 @@ namespace Js
1265
1278
lastSuccessfulMatch = lastActualMatch;
1266
1279
for (int groupId = 0 ; groupId < numGroups; groupId++)
1267
1280
replaceArgs[groupId + 1 ] = GetGroup (scriptContext, pattern, input, nonMatchValue, groupId);
1268
- #pragma prefast(suppress:6386, "The write index numGroups + 1 is in the bound")
1269
1281
replaceArgs[numGroups + 1 ] = JavascriptNumber::ToVar (lastActualMatch.offset , scriptContext);
1270
1282
1271
1283
// The called function must see the global state updated by the current match
@@ -1278,7 +1290,7 @@ namespace Js
1278
1290
ThreadContext* threadContext = scriptContext->GetThreadContext ();
1279
1291
Var replaceVar = threadContext->ExecuteImplicitCall (replacefn, ImplicitCall_Accessor, [=]()->Js ::Var
1280
1292
{
1281
- return replacefn->CallFunction (Arguments (CallInfo ((ushort)( numGroups + 3 )), replaceArgs));
1293
+ return replacefn->CallFunction (Arguments (CallInfo (UInt16Math::Add ( numGroups, 3 )), replaceArgs));
1282
1294
});
1283
1295
JavascriptString* replace = JavascriptConversion::ToString (replaceVar, scriptContext);
1284
1296
concatenated.Append (input, offset, lastActualMatch.offset - offset);
0 commit comments