Skip to content

Commit adc4201

Browse files
Merge commit '5edef4b20babd4c3ddac7460e536f86fd0f2d724' into internal-merge-6.0-2023-03-14-1014
2 parents 1a1a8d3 + 5edef4b commit adc4201

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,6 +2617,9 @@
26172617
<data name="InvalidProgram_Default" xml:space="preserve">
26182618
<value>Common Language Runtime detected an invalid program.</value>
26192619
</data>
2620+
<data name="InvalidTimeZone_InvalidId" xml:space="preserve">
2621+
<value>The time zone ID '{0}' is invalid.</value>
2622+
</data>
26202623
<data name="InvalidTimeZone_InvalidFileData" xml:space="preserve">
26212624
<value>The time zone ID '{0}' was found on the local computer, but the file at '{1}' was corrupt.</value>
26222625
</data>
@@ -2641,6 +2644,9 @@
26412644
<data name="IO_NoFileTableInInMemoryAssemblies" xml:space="preserve">
26422645
<value>This assembly does not have a file table because it was loaded from memory.</value>
26432646
</data>
2647+
<data name="IO_UnseekableFile" xml:space="preserve">
2648+
<value>Unsupported unseekable file.</value>
2649+
</data>
26442650
<data name="IO_EOF_ReadBeyondEOF" xml:space="preserve">
26452651
<value>Unable to read beyond the end of the stream.</value>
26462652
</data>

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,83 @@ private static TimeZoneInfo GetLocalTimeZoneCore()
2424
return GetLocalTimeZoneFromTzFile();
2525
}
2626

27+
internal static byte[] ReadAllBytesFromSeekableNonZeroSizeFile(string path, int maxFileSize)
28+
{
29+
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, FileOptions.SequentialScan))
30+
{
31+
if (!fs.CanSeek)
32+
{
33+
throw new IOException(SR.IO_UnseekableFile);
34+
}
35+
36+
long fileLength = fs.Length;
37+
if (fileLength > maxFileSize)
38+
{
39+
throw new IOException(SR.IO_FileTooLong);
40+
}
41+
42+
if (fileLength == 0)
43+
{
44+
throw new IOException(SR.IO_InvalidReadLength);
45+
}
46+
47+
int index = 0;
48+
int count = (int)fileLength;
49+
byte[] bytes = new byte[count];
50+
while (count > 0)
51+
{
52+
int n = fs.Read(bytes, index, count);
53+
if (n == 0)
54+
{
55+
ThrowHelper.ThrowEndOfFileException();
56+
}
57+
index += n;
58+
count -= n;
59+
}
60+
return bytes;
61+
}
62+
}
63+
64+
// Bitmap covering the ASCII range. The bits is set for the characters [a-z], [A-Z], [0-9], '/', '-', and '_'.
65+
private static byte[] asciiBitmap = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xFF, 0x03, 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07 };
66+
67+
private static bool IdContainsAnyDisallowedChars(string zoneId)
68+
{
69+
for (int i = 0; i < zoneId.Length; i++)
70+
{
71+
int c = zoneId[i];
72+
if (c > 0x7F)
73+
{
74+
return true;
75+
}
76+
77+
int value = c >> 3;
78+
if ((asciiBitmap[value] & (ulong)(1UL << (c - (value << 3)))) == 0)
79+
{
80+
return true;
81+
}
82+
}
83+
84+
return false;
85+
}
86+
2787
private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id, out TimeZoneInfo? value, out Exception? e)
2888
{
2989
value = null;
3090
e = null;
3191

92+
if (Path.IsPathRooted(id) || IdContainsAnyDisallowedChars(id))
93+
{
94+
e = new TimeZoneNotFoundException(SR.Format(SR.InvalidTimeZone_InvalidId, id));
95+
return TimeZoneInfoResult.TimeZoneNotFoundException;
96+
}
97+
3298
string timeZoneDirectory = GetTimeZoneDirectory();
3399
string timeZoneFilePath = Path.Combine(timeZoneDirectory, id);
34100
byte[] rawData;
35101
try
36102
{
37-
rawData = File.ReadAllBytes(timeZoneFilePath);
103+
rawData = ReadAllBytesFromSeekableNonZeroSizeFile(timeZoneFilePath, maxFileSize: 20 * 1024 * 1024 /* 20 MB */); // timezone files usually less than 1 MB.
38104
}
39105
catch (UnauthorizedAccessException ex)
40106
{
@@ -51,7 +117,7 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
51117
e = ex;
52118
return TimeZoneInfoResult.TimeZoneNotFoundException;
53119
}
54-
catch (IOException ex)
120+
catch (Exception ex) when (ex is IOException || ex is OutOfMemoryException)
55121
{
56122
e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath), ex);
57123
return TimeZoneInfoResult.InvalidTimeZoneException;

src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2091,14 +2091,20 @@ public static IEnumerable<object[]> ConvertTime_DateTimeOffset_InvalidDestinatio
20912091
yield return new object[] { s_strPacific + "\\Display" };
20922092
yield return new object[] { s_strPacific + "\n" }; // no trailing newline
20932093
yield return new object[] { new string('a', 100) }; // long string
2094+
yield return new object[] { "/dev/random" };
2095+
yield return new object[] { "Invalid Id" };
2096+
yield return new object[] { "Invalid/Invalid" };
2097+
yield return new object[] { $"./{s_strPacific}" };
2098+
yield return new object[] { $"{s_strPacific}/../{s_strPacific}" };
20942099
}
20952100

20962101
[Theory]
20972102
[MemberData(nameof(ConvertTime_DateTimeOffset_InvalidDestination_TimeZoneNotFoundException_MemberData))]
20982103
public static void ConvertTime_DateTimeOffset_InvalidDestination_TimeZoneNotFoundException(string destinationId)
20992104
{
21002105
DateTimeOffset time1 = new DateTimeOffset(2006, 5, 12, 0, 0, 0, TimeSpan.Zero);
2101-
VerifyConvertException<TimeZoneNotFoundException>(time1, destinationId);
2106+
Exception ex = Record.Exception(() => TimeZoneInfo.ConvertTime(time1, TimeZoneInfo.FindSystemTimeZoneById(destinationId)));
2107+
Assert.True(ex is InvalidTimeZoneException || ex is TimeZoneNotFoundException);
21022108
}
21032109

21042110
[Fact]

0 commit comments

Comments
 (0)