From 72c24296cb01e60387dfb82d4dd0c3ab3de5fff7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:17:57 +0000 Subject: [PATCH 1/3] Initial plan From 0da8f6a531e48d5e14f4c02adb7a03e61cb4731e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:50:03 +0000 Subject: [PATCH 2/3] Fix Uri.TryUnescapeDataString to return false instead of throwing with small destination buffers Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com> --- .../System.Private.Uri/src/System/UriExt.cs | 7 ++++++- .../tests/FunctionalTests/UriEscapingTest.cs | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index d2eecb162f5b3e..0f4fe037e652a8 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -561,10 +561,15 @@ public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Spa vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]); vsb.EnsureCapacity(charsToUnescape.Length - indexOfFirstToUnescape); } - else + else if (destination.Length >= indexOfFirstToUnescape) { vsb = new ValueStringBuilder(destination.Slice(indexOfFirstToUnescape)); } + else + { + charsWritten = 0; + return false; + } UriHelper.Unescape(charsToUnescape.Slice(indexOfFirstToUnescape), ref vsb); diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs index ea4be66631b546..fb5790b73f5a4d 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriEscapingTest.cs @@ -300,6 +300,18 @@ static void ValidateUnescape(string input, string expectedOutput) } } + [Theory] + [InlineData("aa%", 0)] + [InlineData("aa%", 1)] + [InlineData("aaa%41", 0)] + [InlineData("aaa%41", 1)] + [InlineData("aaa%41", 2)] + public void TryUnescapeDataString_SmallDestination_ReturnsFalse(string input, int destinationLength) + { + Assert.False(Uri.TryUnescapeDataString(input, new char[destinationLength], out int charsWritten)); + Assert.Equal(0, charsWritten); + } + [Fact] public void UriEscapeUnescapeDataString_LongInputs() { From fd9482915302e6efbc1c625fa38197312c359e67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:16:42 +0000 Subject: [PATCH 3/3] Move destination length check before overlapped check Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com> --- .../System.Private.Uri/src/System/UriExt.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index 0f4fe037e652a8..e7681c2a140a1e 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -548,6 +548,12 @@ public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Spa return false; } + if (destination.Length < indexOfFirstToUnescape) + { + charsWritten = 0; + return false; + } + // We may throw for very large inputs (when growing the ValueStringBuilder). scoped ValueStringBuilder vsb; @@ -561,14 +567,9 @@ public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Spa vsb = new ValueStringBuilder(stackalloc char[StackallocThreshold]); vsb.EnsureCapacity(charsToUnescape.Length - indexOfFirstToUnescape); } - else if (destination.Length >= indexOfFirstToUnescape) - { - vsb = new ValueStringBuilder(destination.Slice(indexOfFirstToUnescape)); - } else { - charsWritten = 0; - return false; + vsb = new ValueStringBuilder(destination.Slice(indexOfFirstToUnescape)); } UriHelper.Unescape(charsToUnescape.Slice(indexOfFirstToUnescape), ref vsb);