Fix porting from servicing release (#83633)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Mon, 20 Mar 2023 18:08:42 +0000 (11:08 -0700)
committerGitHub <noreply@github.com>
Mon, 20 Mar 2023 18:08:42 +0000 (11:08 -0700)
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs
src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs

index e7dc54f16d19408380432a783be6cd5a0e41eb95..bbf9c3f213d980b22c47d987c495575ec2125db1 100644 (file)
   <data name="InvalidProgram_Default" xml:space="preserve">
     <value>Common Language Runtime detected an invalid program.</value>
   </data>
+  <data name="InvalidTimeZone_InvalidId" xml:space="preserve">
+    <value>The time zone ID '{0}' is invalid.</value>
+  </data>
   <data name="InvalidTimeZone_InvalidFileData" xml:space="preserve">
     <value>The time zone ID '{0}' was found on the local computer, but the file at '{1}' was corrupt.</value>
   </data>
   <data name="IO_NoFileTableInInMemoryAssemblies" xml:space="preserve">
     <value>This assembly does not have a file table because it was loaded from memory.</value>
   </data>
+  <data name="IO_UnseekableFile" xml:space="preserve">
+    <value>Unsupported unseekable file.</value>
+  </data>
   <data name="IO_EOF_ReadBeyondEOF" xml:space="preserve">
     <value>Unable to read beyond the end of the stream.</value>
   </data>
index 45eb47885ba812bf2d34566dd768ca672e63bb78..f90b52bdc520537da0bebf1eeab3af5a222dd880 100644 (file)
@@ -29,11 +29,55 @@ namespace System
             return GetLocalTimeZoneFromTzFile();
         }
 
+        private static byte[] ReadAllBytesFromSeekableNonZeroSizeFile(string path, int maxFileSize)
+        {
+            using FileStream fs = File.OpenRead(path);
+            if (!fs.CanSeek)
+            {
+                throw new IOException(SR.IO_UnseekableFile);
+            }
+
+            if (fs.Length == 0 || fs.Length > maxFileSize)
+            {
+                throw new IOException(fs.Length == 0 ? SR.IO_InvalidReadLength : SR.IO_FileTooLong);
+            }
+
+            byte[] bytes = new byte[fs.Length];
+            fs.ReadExactly(bytes, 0, bytes.Length);
+            return bytes;
+        }
+
+        // Bitmap covering the ASCII range. The bits is set for the characters [a-z], [A-Z], [0-9], '/', '-', and '_'.
+        private static byte[] asciiBitmap = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xFF, 0x03, 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07 };
+        private static bool IdContainsAnyDisallowedChars(string zoneId)
+        {
+            for (int i = 0; i < zoneId.Length; i++)
+            {
+                int c = zoneId[i];
+                if (c > 0x7F)
+                {
+                    return true;
+                }
+                int value = c >> 3;
+                if ((asciiBitmap[value] & (ulong)(1UL << (c - (value << 3)))) == 0)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id, out TimeZoneInfo? value, out Exception? e)
         {
             value = null;
             e = null;
 
+            if (Path.IsPathRooted(id) || IdContainsAnyDisallowedChars(id))
+            {
+                e = new TimeZoneNotFoundException(SR.Format(SR.InvalidTimeZone_InvalidId, id));
+                return TimeZoneInfoResult.TimeZoneNotFoundException;
+            }
+
             byte[]? rawData=null;
             string timeZoneDirectory = GetTimeZoneDirectory();
             string timeZoneFilePath = Path.Combine(timeZoneDirectory, id);
@@ -61,7 +105,7 @@ namespace System
 
             try
             {
-                rawData = File.ReadAllBytes(timeZoneFilePath);
+                rawData = ReadAllBytesFromSeekableNonZeroSizeFile(timeZoneFilePath, maxFileSize: 20 * 1024 * 1024 /* 20 MB */); // timezone files usually less than 1 MB.
             }
             catch (UnauthorizedAccessException ex)
             {
@@ -78,7 +122,7 @@ namespace System
                 e = ex;
                 return TimeZoneInfoResult.TimeZoneNotFoundException;
             }
-            catch (IOException ex)
+            catch (Exception ex) when (ex is IOException || ex is OutOfMemoryException)
             {
                 e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, timeZoneFilePath), ex);
                 return TimeZoneInfoResult.InvalidTimeZoneException;
index d941efb83da8daac272a515ad93fd36d4560eb1f..b2369f5842551b0b8074f6e3d69cf3811f942b95 100644 (file)
@@ -2083,6 +2083,11 @@ namespace System.Tests
             yield return new object[] { s_strPacific + "\\Display" };
             yield return new object[] { s_strPacific + "\n" }; // no trailing newline
             yield return new object[] { new string('a', 100) }; // long string
+            yield return new object[] { "/dev/random" };
+            yield return new object[] { "Invalid Id" };
+            yield return new object[] { "Invalid/Invalid" };
+            yield return new object[] { $"./{s_strPacific}" };
+            yield return new object[] { $"{s_strPacific}/../{s_strPacific}" };
         }
 
         [Theory]