Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 1ee83de

Browse files
maryamariyandanmoseley
authored andcommitted
Fixes StringBuilder unbounded size growth in Clear() when we use a mix of Append and Insert (#16926)
* Fixing Clear infinitely growing when we combine usage of Insert and Append. Fixes #27625 * Move debug code to StringBuilder.Debug.cs and Applied code review feedback * Adding missing debug condition check * Adding comments and moving Condition on projitems after filename * Moving the infinite capacity growth fix to Length setter * Removing originalCapacity and Debug.Assert * Applying PR feedbacks * Minor cleanup * simplifying to single loop * keeping just one method for ShowChunks function
1 parent a17fa33 commit 1ee83de

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

src/mscorlib/shared/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@
521521
<Compile Include="$(MSBuildThisFileDirectory)System\Text\Latin1Encoding.cs" />
522522
<Compile Include="$(MSBuildThisFileDirectory)System\Text\NormalizationForm.cs" />
523523
<Compile Include="$(MSBuildThisFileDirectory)System\Text\StringBuilder.cs" />
524+
<Compile Include="$(MSBuildThisFileDirectory)System\Text\StringBuilder.Debug.cs" Condition="'$(Configuration)' == 'Debug'" />
524525
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UnicodeEncoding.cs" />
525526
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF32Encoding.cs" />
526527
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" />
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Runtime.Serialization;
8+
9+
namespace System.Text
10+
{
11+
public sealed partial class StringBuilder
12+
{
13+
private void ShowChunks(int maxChunksToShow = 10)
14+
{
15+
int count = 0;
16+
StringBuilder head = this, current = this;
17+
while (current != null)
18+
{
19+
if (count < maxChunksToShow)
20+
{
21+
count++;
22+
}
23+
else
24+
{
25+
head = head.m_ChunkPrevious;
26+
}
27+
current = current.m_ChunkPrevious;
28+
}
29+
current = head;
30+
string[] chunks = new string[count];
31+
for (int i = count; i > 0; i--)
32+
{
33+
chunks[i - 1] = new string(current.m_ChunkChars).Replace('\0', '.');
34+
current = current.m_ChunkPrevious;
35+
}
36+
Debug.WriteLine('|' + string.Join('|', chunks) + '|');
37+
}
38+
}
39+
}

src/mscorlib/shared/System/Text/StringBuilder.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,10 @@ public int Length
462462
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
463463
}
464464

465-
int originalCapacity = Capacity;
466-
467465
if (value == 0 && m_ChunkPrevious == null)
468466
{
469467
m_ChunkLength = 0;
470468
m_ChunkOffset = 0;
471-
Debug.Assert(Capacity >= originalCapacity);
472469
return;
473470
}
474471

@@ -485,7 +482,10 @@ public int Length
485482
{
486483
// We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk,
487484
// to ensure the original capacity is preserved.
488-
int newLen = originalCapacity - chunk.m_ChunkOffset;
485+
486+
// Avoid possible infinite capacity growth. See https://github.com/dotnet/coreclr/pull/16926
487+
int capacityToPreserve = Math.Min(Capacity, Math.Max(Length * 6 / 5, m_ChunkChars.Length));
488+
int newLen = capacityToPreserve - chunk.m_ChunkOffset;
489489
char[] newArray = new char[newLen];
490490

491491
Debug.Assert(newLen > chunk.m_ChunkChars.Length, "The new chunk should be larger than the one it is replacing.");
@@ -498,7 +498,6 @@ public int Length
498498
m_ChunkLength = value - chunk.m_ChunkOffset;
499499
AssertInvariants();
500500
}
501-
Debug.Assert(Capacity >= originalCapacity);
502501
}
503502
}
504503

0 commit comments

Comments
 (0)