Skip to content

Commit 928eb5f

Browse files
authored
Remove unsafe code from Uri PrivateParseMinimal, CheckAuthorityHelper, and related helpers (#121671)
1 parent fa6bf38 commit 928eb5f

5 files changed

Lines changed: 306 additions & 419 deletions

File tree

src/libraries/System.Private.Uri/src/System/IriHelper.cs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,62 +69,55 @@ internal static bool CheckIriUnicodeRange(uint value, bool isQuery)
6969
public static bool IsInInclusiveRange(uint value, uint min, uint max)
7070
=> (value - min) <= (max - min);
7171

72-
//
7372
// IRI normalization for strings containing characters that are not allowed or
7473
// escaped characters that should be unescaped in the context of the specified Uri component.
75-
//
76-
internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end, bool isQuery)
74+
internal static string EscapeUnescapeIri(ReadOnlySpan<char> span, bool isQuery)
7775
{
78-
Debug.Assert(end >= 0 && start >= 0 && start <= end);
79-
80-
int size = end - start;
81-
var dest = size <= Uri.StackallocThreshold
76+
var dest = span.Length <= Uri.StackallocThreshold
8277
? new ValueStringBuilder(stackalloc char[Uri.StackallocThreshold])
83-
: new ValueStringBuilder(size);
78+
: new ValueStringBuilder(span.Length);
8479

8580
Span<byte> maxUtf8EncodedSpan = stackalloc byte[4];
8681

87-
for (int i = start; i < end; ++i)
82+
for (int i = 0; (uint)i < (uint)span.Length; i++)
8883
{
89-
char ch = pInput[i];
84+
char ch = span[i];
85+
9086
if (ch == '%')
9187
{
92-
if (end - i > 2)
88+
if ((uint)(i + 2) < (uint)span.Length)
9389
{
94-
ch = UriHelper.DecodeHexChars(pInput[i + 1], pInput[i + 2]);
90+
ch = UriHelper.DecodeHexChars(span[i + 1], span[i + 2]);
9591

9692
// Do not unescape a reserved char
9793
if (ch == Uri.c_DummyChar || UriHelper.IsNotSafeForUnescape(ch))
9894
{
9995
// keep as is
100-
dest.Append(pInput[i++]);
101-
dest.Append(pInput[i++]);
102-
dest.Append(pInput[i]);
103-
continue;
96+
dest.Append(span[i]);
97+
dest.Append(span[i + 1]);
98+
dest.Append(span[i + 2]);
99+
i += 2;
104100
}
105101
else if (ch <= '\x7F')
106102
{
107103
// ASCII
108104
dest.Append(ch);
109105
i += 2;
110-
continue;
111106
}
112107
else
113108
{
114109
// possibly utf8 encoded sequence of unicode
115110
int charactersRead = PercentEncodingHelper.UnescapePercentEncodedUTF8Sequence(
116-
new ReadOnlySpan<char>(pInput + i, end - i),
111+
span.Slice(i),
117112
ref dest,
118113
isQuery,
119114
iriParsing: true);
120115

121116
Debug.Assert(charactersRead > 0);
122117
i += charactersRead - 1; // -1 as i will be incremented in the loop
123118
}
124-
}
125-
else
126-
{
127-
dest.Append(pInput[i]);
119+
120+
continue;
128121
}
129122
}
130123
else if (ch > '\x7f')
@@ -136,9 +129,9 @@ internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end
136129

137130
char ch2 = '\0';
138131

139-
if ((char.IsHighSurrogate(ch)) && (i + 1 < end))
132+
if ((char.IsHighSurrogate(ch)) && (uint)(i + 1) < (uint)span.Length)
140133
{
141-
ch2 = pInput[i + 1];
134+
ch2 = span[i + 1];
142135
isInIriUnicodeRange = CheckIriUnicodeRange(ch, ch2, out surrogatePair, isQuery);
143136
}
144137
else
@@ -179,12 +172,12 @@ internal static unsafe string EscapeUnescapeIri(char* pInput, int start, int end
179172
{
180173
i++;
181174
}
175+
176+
continue;
182177
}
183-
else
184-
{
185-
// ASCII, just copy the character
186-
dest.Append(pInput[i]);
187-
}
178+
179+
// ASCII, just copy the character
180+
dest.Append(ch);
188181
}
189182

190183
return dest.ToString();

src/libraries/System.Private.Uri/src/System/UncNameHelper.cs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,27 @@ public static string ParseCanonicalName(string str, int start, int end, ref bool
3636
//
3737
// Assumption is the caller will check on the resulting name length
3838
// Remarks: MUST NOT be used unless all input indexes are verified and trusted.
39-
public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bool notImplicitFile)
39+
public static bool IsValid(ReadOnlySpan<char> name, bool notImplicitFile, out int nameLength)
4040
{
41-
int end = returnedEnd;
41+
nameLength = 0;
4242

43-
if (start == end)
44-
return false;
45-
//
4643
// First segment could consist of only '_' or '-' but it cannot be all digits or empty
47-
//
4844
bool validShortName = false;
49-
int i = start;
50-
for (; i < end; ++i)
45+
int i = 0;
46+
for (; i < name.Length; i++)
5147
{
52-
if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
48+
if (char.IsLetter(name[i]) || name[i] == '-' || name[i] == '_')
5349
{
54-
end = i;
55-
break;
50+
validShortName = true;
5651
}
57-
else if (name[i] == '.')
52+
else if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
5853
{
59-
++i;
6054
break;
6155
}
62-
63-
if (char.IsLetter(name[i]) || name[i] == '-' || name[i] == '_')
56+
else if (name[i] == '.')
6457
{
65-
validShortName = true;
58+
i++;
59+
break;
6660
}
6761
else if (!char.IsAsciiDigit(name[i]))
6862
{
@@ -73,20 +67,17 @@ public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bo
7367
if (!validShortName)
7468
return false;
7569

76-
//
7770
// Subsequent segments must start with a letter or a digit
78-
//
7971

80-
for (; i < end; ++i)
72+
for (; (uint)i < (uint)name.Length; i++)
8173
{
8274
if (name[i] == '/' || name[i] == '\\' || (notImplicitFile && (name[i] == ':' || name[i] == '?' || name[i] == '#')))
8375
{
84-
end = i;
8576
break;
8677
}
8778
else if (name[i] == '.')
8879
{
89-
if (!validShortName || ((i - 1) >= start && name[i - 1] == '.'))
80+
if (!validShortName || name[i - 1] == '.')
9081
return false;
9182

9283
validShortName = false;
@@ -98,23 +89,25 @@ public static unsafe bool IsValid(char* name, int start, ref int returnedEnd, bo
9889
}
9990
else if (char.IsLetter(name[i]) || char.IsAsciiDigit(name[i]))
10091
{
101-
if (!validShortName)
102-
validShortName = true;
92+
validShortName = true;
10393
}
10494
else
95+
{
10596
return false;
97+
}
10698
}
10799

108-
// last segment can end with the dot
109-
if (((i - 1) >= start && name[i - 1] == '.'))
110-
validShortName = true;
111-
112100
if (!validShortName)
113-
return false;
114-
115-
// caller must check for (end - start <= MaximumInternetNameLength)
101+
{
102+
// last segment can end with the dot
103+
if ((uint)(i - 1) >= (uint)name.Length || name[i - 1] != '.')
104+
{
105+
return false;
106+
}
107+
}
116108

117-
returnedEnd = end;
109+
// Caller must check that (nameLength <= MaximumInternetNameLength)
110+
nameLength = i;
118111
return true;
119112
}
120113
}

0 commit comments

Comments
 (0)