@@ -30,6 +30,7 @@ pub struct TokenStream<'a> {
30
30
position : Pos ,
31
31
off : usize ,
32
32
next_state : Option < ( usize , Token < ' a > , usize , Pos ) > ,
33
+ recursion_limit : usize ,
33
34
}
34
35
35
36
#[ derive( Clone , Debug , PartialEq ) ]
@@ -53,7 +54,7 @@ impl<'a> StreamOnce for TokenStream<'a> {
53
54
}
54
55
}
55
56
let old_pos = self . off ;
56
- let ( kind, len) = self . peek_token ( ) ?;
57
+ let ( kind, len) = self . take_token ( ) ?;
57
58
let value = & self . buf [ self . off -len..self . off ] ;
58
59
self . skip_whitespace ( ) ;
59
60
let token = Token { kind, value } ;
@@ -125,17 +126,34 @@ fn check_float(value: &str, exponent: Option<usize>, real: Option<usize>)
125
126
126
127
impl < ' a > TokenStream < ' a > {
127
128
pub fn new ( s : & str ) -> TokenStream {
129
+ Self :: with_recursion_limit ( s, 50 )
130
+ }
131
+
132
+ /// Specify a limit to recursive parsing. Note that increasing the limit
133
+ /// from the default may represent a security issue since a maliciously
134
+ /// crafted input may cause a stack overflow, crashing the process.
135
+ pub ( crate ) fn with_recursion_limit ( s : & str , recursion_limit : usize ) -> TokenStream {
128
136
let mut me = TokenStream {
129
137
buf : s,
130
138
position : Pos { line : 1 , column : 1 } ,
131
139
off : 0 ,
132
140
next_state : None ,
141
+ recursion_limit
133
142
} ;
134
143
me. skip_whitespace ( ) ;
135
144
me
136
145
}
137
146
138
- fn peek_token ( & mut self )
147
+ /// Convenience for the common case where a token does
148
+ /// not span multiple lines. Infallible.
149
+ #[ inline]
150
+ fn advance_token < T > ( & mut self , kind : Kind , size : usize ) -> Result < ( Kind , usize ) , T > {
151
+ self . position . column += size;
152
+ self . off += size;
153
+ Ok ( ( kind, size) )
154
+ }
155
+
156
+ fn take_token ( & mut self )
139
157
-> Result < ( Kind , usize ) , Error < Token < ' a > , Token < ' a > > >
140
158
{
141
159
use self :: Kind :: * ;
@@ -146,19 +164,32 @@ impl<'a> TokenStream<'a> {
146
164
} ;
147
165
148
166
match cur_char {
149
- '!' | '$' | ':' | '=' | '@' | '|' |
150
- '(' | ')' | '[' | ']' | '{' | '}' | '&' => {
151
- self . position . column += 1 ;
152
- self . off += 1 ;
153
-
154
- Ok ( ( Punctuator , 1 ) )
167
+ '(' | '[' | '{' => {
168
+ // Check for recursion limit
169
+ self . recursion_limit = self . recursion_limit
170
+ . checked_sub ( 1 )
171
+ . ok_or_else ( || Error :: message_static_message ( "Recursion limit exceeded" ) ) ?;
172
+
173
+ self . advance_token ( Punctuator , 1 )
174
+ } ,
175
+ ')' | ']' | '}' => {
176
+ // Notes on exceptional cases:
177
+ // recursion_limit may exceed the original value specified
178
+ // when constructing the Tokenizer. It may at first
179
+ // seem like this would be a good place to handle that,
180
+ // but instead this code allows this token to propagate up
181
+ // to the parser which is better equipped to make specific
182
+ // error messages about unmatched pairs.
183
+ // The case where recursion limit would overflow but instead
184
+ // saturates is just a specific case of the more general
185
+ // occurrence above.
186
+ self . recursion_limit = self . recursion_limit . saturating_add ( 1 ) ;
187
+ self . advance_token ( Punctuator , 1 )
155
188
}
189
+ '!' | '$' | ':' | '=' | '@' | '|' | '&' => self . advance_token ( Punctuator , 1 ) ,
156
190
'.' => {
157
191
if iter. as_str ( ) . starts_with ( ".." ) {
158
- self . position . column += 3 ;
159
- self . off += 3 ;
160
-
161
- Ok ( ( Punctuator , 3 ) )
192
+ self . advance_token ( Punctuator , 3 )
162
193
} else {
163
194
Err (
164
195
Error :: unexpected_message (
@@ -172,11 +203,7 @@ impl<'a> TokenStream<'a> {
172
203
while let Some ( ( idx, cur_char) ) = iter. next ( ) {
173
204
match cur_char {
174
205
'_' | 'a' ..='z' | 'A' ..='Z' | '0' ..='9' => continue ,
175
- _ => {
176
- self . position . column += idx;
177
- self . off += idx;
178
- return Ok ( ( Name , idx) ) ;
179
- }
206
+ _ => return self . advance_token ( Name , idx) ,
180
207
}
181
208
}
182
209
let len = self . buf . len ( ) - self . off ;
@@ -227,10 +254,7 @@ impl<'a> TokenStream<'a> {
227
254
)
228
255
) ;
229
256
}
230
- self . position . column += len;
231
- self . off += len;
232
-
233
- Ok ( ( IntValue , len) )
257
+ self . advance_token ( IntValue , len)
234
258
}
235
259
}
236
260
'"' => {
0 commit comments