@@ -24,17 +24,61 @@ private static TimeZoneInfo GetLocalTimeZoneCore()
24
24
return GetLocalTimeZoneFromTzFile ( ) ;
25
25
}
26
26
27
+ private static byte [ ] ReadAllBytesFromSeekableNonZeroSizeFile ( string path , int maxFileSize )
28
+ {
29
+ using FileStream fs = File . OpenRead ( path ) ;
30
+ if ( ! fs . CanSeek )
31
+ {
32
+ throw new IOException ( SR . IO_UnseekableFile ) ;
33
+ }
34
+
35
+ if ( fs . Length == 0 || fs . Length > maxFileSize )
36
+ {
37
+ throw new IOException ( fs . Length == 0 ? SR . IO_InvalidReadLength : SR . IO_FileTooLong ) ;
38
+ }
39
+
40
+ byte [ ] bytes = new byte [ fs . Length ] ;
41
+ fs . ReadExactly ( bytes , 0 , bytes . Length ) ;
42
+ return bytes ;
43
+ }
44
+
45
+ // Bitmap covering the ASCII range. The bits is set for the characters [a-z], [A-Z], [0-9], '/', '-', and '_'.
46
+ private static byte [ ] asciiBitmap = new byte [ ] { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xA8 , 0xFF , 0x03 , 0xFE , 0xFF , 0xFF , 0x87 , 0xFE , 0xFF , 0xFF , 0x07 } ;
47
+ private static bool IdContainsAnyDisallowedChars ( string zoneId )
48
+ {
49
+ for ( int i = 0 ; i < zoneId . Length ; i ++ )
50
+ {
51
+ int c = zoneId [ i ] ;
52
+ if ( c > 0x7F )
53
+ {
54
+ return true ;
55
+ }
56
+ int value = c >> 3 ;
57
+ if ( ( asciiBitmap [ value ] & ( ulong ) ( 1UL << ( c - ( value << 3 ) ) ) ) == 0 )
58
+ {
59
+ return true ;
60
+ }
61
+ }
62
+ return false ;
63
+ }
64
+
27
65
private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore ( string id , out TimeZoneInfo ? value , out Exception ? e )
28
66
{
29
67
value = null ;
30
68
e = null ;
31
69
70
+ if ( Path . IsPathRooted ( id ) || IdContainsAnyDisallowedChars ( id ) )
71
+ {
72
+ e = new TimeZoneNotFoundException ( SR . Format ( SR . InvalidTimeZone_InvalidId , id ) ) ;
73
+ return TimeZoneInfoResult . TimeZoneNotFoundException ;
74
+ }
75
+
32
76
string timeZoneDirectory = GetTimeZoneDirectory ( ) ;
33
77
string timeZoneFilePath = Path . Combine ( timeZoneDirectory , id ) ;
34
78
byte [ ] rawData ;
35
79
try
36
80
{
37
- rawData = File . ReadAllBytes ( timeZoneFilePath ) ;
81
+ rawData = ReadAllBytesFromSeekableNonZeroSizeFile ( timeZoneFilePath , maxFileSize : 20 * 1024 * 1024 /* 20 MB */ ) ; // timezone files usually less than 1 MB.
38
82
}
39
83
catch ( UnauthorizedAccessException ex )
40
84
{
@@ -51,7 +95,7 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
51
95
e = ex ;
52
96
return TimeZoneInfoResult . TimeZoneNotFoundException ;
53
97
}
54
- catch ( IOException ex )
98
+ catch ( Exception ex ) when ( ex is IOException || ex is OutOfMemoryException )
55
99
{
56
100
e = new InvalidTimeZoneException ( SR . Format ( SR . InvalidTimeZone_InvalidFileData , id , timeZoneFilePath ) , ex ) ;
57
101
return TimeZoneInfoResult . InvalidTimeZoneException ;
0 commit comments