@@ -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 ;
0 commit comments