@@ -29,11 +29,55 @@ private static TimeZoneInfo GetLocalTimeZoneCore()
2929 return GetLocalTimeZoneFromTzFile ( ) ;
3030 }
3131
32+ private static byte [ ] ReadAllBytesFromSeekableNonZeroSizeFile ( string path , int maxFileSize )
33+ {
34+ using FileStream fs = File . OpenRead ( path ) ;
35+ if ( ! fs . CanSeek )
36+ {
37+ throw new IOException ( SR . IO_UnseekableFile ) ;
38+ }
39+
40+ if ( fs . Length == 0 || fs . Length > maxFileSize )
41+ {
42+ throw new IOException ( fs . Length == 0 ? SR . IO_InvalidReadLength : SR . IO_FileTooLong ) ;
43+ }
44+
45+ byte [ ] bytes = new byte [ fs . Length ] ;
46+ fs . ReadExactly ( bytes , 0 , bytes . Length ) ;
47+ return bytes ;
48+ }
49+
50+ // Bitmap covering the ASCII range. The bits is set for the characters [a-z], [A-Z], [0-9], '/', '-', and '_'.
51+ private static byte [ ] asciiBitmap = new byte [ ] { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xA8 , 0xFF , 0x03 , 0xFE , 0xFF , 0xFF , 0x87 , 0xFE , 0xFF , 0xFF , 0x07 } ;
52+ private static bool IdContainsAnyDisallowedChars ( string zoneId )
53+ {
54+ for ( int i = 0 ; i < zoneId . Length ; i ++ )
55+ {
56+ int c = zoneId [ i ] ;
57+ if ( c > 0x7F )
58+ {
59+ return true ;
60+ }
61+ int value = c >> 3 ;
62+ if ( ( asciiBitmap [ value ] & ( ulong ) ( 1UL << ( c - ( value << 3 ) ) ) ) == 0 )
63+ {
64+ return true ;
65+ }
66+ }
67+ return false ;
68+ }
69+
3270 private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore ( string id , out TimeZoneInfo ? value , out Exception ? e )
3371 {
3472 value = null ;
3573 e = null ;
3674
75+ if ( Path . IsPathRooted ( id ) || IdContainsAnyDisallowedChars ( id ) )
76+ {
77+ e = new TimeZoneNotFoundException ( SR . Format ( SR . InvalidTimeZone_InvalidId , id ) ) ;
78+ return TimeZoneInfoResult . TimeZoneNotFoundException ;
79+ }
80+
3781 byte [ ] ? rawData = null ;
3882 string timeZoneDirectory = GetTimeZoneDirectory ( ) ;
3983 string timeZoneFilePath = Path . Combine ( timeZoneDirectory , id ) ;
@@ -61,7 +105,7 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
61105
62106 try
63107 {
64- rawData = File . ReadAllBytes ( timeZoneFilePath ) ;
108+ rawData = ReadAllBytesFromSeekableNonZeroSizeFile ( timeZoneFilePath , maxFileSize : 20 * 1024 * 1024 /* 20 MB */ ) ; // timezone files usually less than 1 MB.
65109 }
66110 catch ( UnauthorizedAccessException ex )
67111 {
@@ -78,7 +122,7 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
78122 e = ex ;
79123 return TimeZoneInfoResult . TimeZoneNotFoundException ;
80124 }
81- catch ( IOException ex )
125+ catch ( Exception ex ) when ( ex is IOException || ex is OutOfMemoryException )
82126 {
83127 e = new InvalidTimeZoneException ( SR . Format ( SR . InvalidTimeZone_InvalidFileData , id , timeZoneFilePath ) , ex ) ;
84128 return TimeZoneInfoResult . InvalidTimeZoneException ;
0 commit comments