diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 929ee6132d851..da4412098aa6f 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4220,4 +4220,7 @@ An unexpected state object was encountered. This is usually a sign of a bug in async method custom infrastructure, such as a custom awaiter or IValueTaskSource implementation. + + The resulting string is too long + diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index 7a8dac5644f83..77251e080cb55 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -38,14 +38,11 @@ internal static class SearchValuesStorage internal const int StackallocIntBufferSizeLimit = 128; internal const int StackallocCharBufferSizeLimit = 256; - private static void FillStringChecked(string dest, int destPos, string src) + private static void CopyStringContent(string dest, int destPos, string src) { Debug.Assert(dest != null); Debug.Assert(src != null); - if (src.Length > dest.Length - destPos) - { - throw new IndexOutOfRangeException(); - } + Debug.Assert(src.Length <= dest.Length - destPos); Buffer.Memmove( destination: ref Unsafe.Add(ref dest._firstChar, destPos), @@ -97,7 +94,7 @@ public static string Concat(params object?[] args) if (totalLength < 0) // Check for a positive overflow { - throw new OutOfMemoryException(); + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); } } @@ -117,7 +114,7 @@ public static string Concat(params object?[] args) Debug.Assert(s != null); Debug.Assert(position <= totalLength - s.Length, "We didn't allocate enough space for the result string!"); - FillStringChecked(result, position, s); + CopyStringContent(result, position, s); position += s.Length; } @@ -255,10 +252,18 @@ public static string Concat(string? str0, string? str1) int str0Length = str0.Length; - string result = FastAllocateString(str0Length + str1.Length); + int totalLength = str0Length + str1.Length; + + // Can't overflow to a positive number so just check < 0 + if (totalLength < 0) + { + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); + } + + string result = FastAllocateString(totalLength); - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0Length, str1); + CopyStringContent(result, 0, str0); + CopyStringContent(result, str0Length, str1); return result; } @@ -280,12 +285,18 @@ public static string Concat(string? str0, string? str1, string? str2) return Concat(str0, str1); } - int totalLength = str0.Length + str1.Length + str2.Length; + // It can overflow to a positive number so we accumulate the total length as a long. + long totalLength = (long)str0.Length + (long)str1.Length + (long)str2.Length; - string result = FastAllocateString(totalLength); - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0.Length, str1); - FillStringChecked(result, str0.Length + str1.Length, str2); + if (totalLength > int.MaxValue) + { + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); + } + + string result = FastAllocateString((int)totalLength); + CopyStringContent(result, 0, str0); + CopyStringContent(result, str0.Length, str1); + CopyStringContent(result, str0.Length + str1.Length, str2); return result; } @@ -312,13 +323,19 @@ public static string Concat(string? str0, string? str1, string? str2, string? st return Concat(str0, str1, str2); } - int totalLength = str0.Length + str1.Length + str2.Length + str3.Length; + // It can overflow to a positive number so we accumulate the total length as a long. + long totalLength = (long)str0.Length + (long)str1.Length + (long)str2.Length + (long)str3.Length; - string result = FastAllocateString(totalLength); - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0.Length, str1); - FillStringChecked(result, str0.Length + str1.Length, str2); - FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3); + if (totalLength > int.MaxValue) + { + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); + } + + string result = FastAllocateString((int)totalLength); + CopyStringContent(result, 0, str0); + CopyStringContent(result, str0.Length, str1); + CopyStringContent(result, str0.Length + str1.Length, str2); + CopyStringContent(result, str0.Length + str1.Length + str2.Length, str3); return result; } @@ -447,7 +464,7 @@ public static string Concat(params string?[] values) // If it's too long, fail, or if it's empty, return an empty string. if (totalLengthLong > int.MaxValue) { - throw new OutOfMemoryException(); + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); } int totalLength = (int)totalLengthLong; if (totalLength == 0) @@ -470,7 +487,7 @@ public static string Concat(params string?[] values) break; } - FillStringChecked(result, copiedLength, value); + CopyStringContent(result, copiedLength, value); copiedLength += valueLen; } } @@ -931,7 +948,7 @@ private static string JoinCore(ReadOnlySpan separator, ReadOnlySpan int.MaxValue) { - ThrowHelper.ThrowOutOfMemoryException(); + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); } int totalLength = (int)totalSeparatorsLength; @@ -943,7 +960,7 @@ private static string JoinCore(ReadOnlySpan separator, ReadOnlySpan separator, ReadOnlySpan int.MaxValue) - throw new OutOfMemoryException(); + ThrowHelper.ThrowOutOfMemoryException_StringTooLong(); string dst = FastAllocateString((int)dstLength); Span dstSpan = new Span(ref dst._firstChar, dst.Length); diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index d380e9d7533d7..95c49059600b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -443,6 +443,12 @@ internal static void ThrowOutOfMemoryException() throw new OutOfMemoryException(); } + [DoesNotReturn] + internal static void ThrowOutOfMemoryException_StringTooLong() + { + throw new OutOfMemoryException(SR.OutOfMemory_StringTooLong); + } + [DoesNotReturn] internal static void ThrowArgumentException_Argument_IncompatibleArrayType() {