Skip to content

Commit

Permalink
Merge pull request #163 from serde-rs/limit
Browse files Browse the repository at this point in the history
Limit recursion to 128 levels
  • Loading branch information
dtolnay authored Dec 3, 2016
2 parents b706bc3 + 4d57ebe commit 3516a90
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
39 changes: 33 additions & 6 deletions json/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl<Iter> de::Deserializer for Deserializer<Iter>
struct DeserializerImpl<R: Read> {
read: R,
str_buf: Vec<u8>,
remaining_depth: u8,
}

macro_rules! overflow {
Expand All @@ -107,6 +108,7 @@ impl<R: Read> DeserializerImpl<R> {
DeserializerImpl {
read: read,
str_buf: Vec::with_capacity(128),
remaining_depth: 128,
}
}

Expand Down Expand Up @@ -205,12 +207,30 @@ impl<R: Read> DeserializerImpl<R> {
visitor.visit_str(s)
}
b'[' => {
self.remaining_depth -= 1;
if self.remaining_depth == 0 {
return Err(self.peek_error(stack_overflow()));
}

self.eat_char();
visitor.visit_seq(SeqVisitor::new(self))
let ret = visitor.visit_seq(SeqVisitor::new(self));

self.remaining_depth += 1;

ret
}
b'{' => {
self.remaining_depth -= 1;
if self.remaining_depth == 0 {
return Err(self.peek_error(stack_overflow()));
}

self.eat_char();
visitor.visit_map(MapVisitor::new(self))
let ret = visitor.visit_map(MapVisitor::new(self));

self.remaining_depth += 1;

ret
}
_ => Err(self.peek_error(ErrorCode::ExpectedSomeValue)),
};
Expand Down Expand Up @@ -523,6 +543,10 @@ impl<R: Read> DeserializerImpl<R> {
}
}

fn stack_overflow() -> ErrorCode {
ErrorCode::Custom("recursion limit exceeded".into())
}

static POW10: [f64; 309] =
[1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
1e010, 1e011, 1e012, 1e013, 1e014, 1e015, 1e016, 1e017, 1e018, 1e019,
Expand Down Expand Up @@ -610,12 +634,15 @@ impl<R: Read> de::Deserializer for DeserializerImpl<R> {

match try!(self.peek_or_null()) {
b'{' => {
self.remaining_depth -= 1;
if self.remaining_depth == 0 {
return Err(self.peek_error(stack_overflow()));
}

self.eat_char();
try!(self.parse_whitespace());
let value = try!(visitor.visit(VariantVisitor::new(self)));

let value = {
try!(visitor.visit(VariantVisitor::new(self)))
};
self.remaining_depth += 1;

try!(self.parse_whitespace());

Expand Down
14 changes: 13 additions & 1 deletion json_tests/tests/test_json.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::f64;
use std::fmt::Debug;
use std::i64;
use std::iter;
use std::marker::PhantomData;
use std::u64;

Expand Down Expand Up @@ -737,7 +738,7 @@ macro_rules! test_parse_err {
}

// FIXME (#5527): these could be merged once UFCS is finished.
fn test_parse_err<T>(errors: Vec<(&'static str, Error)>)
fn test_parse_err<T>(errors: Vec<(&str, Error)>)
where T: Debug + PartialEq + de::Deserialize,
{
for &(s, ref err) in &errors {
Expand Down Expand Up @@ -1632,3 +1633,14 @@ fn test_json_pointer() {
assert!(data.pointer("/foo/00").is_none());
assert!(data.pointer("/foo/01").is_none());
}

#[test]
fn test_stack_overflow() {
let brackets: String = iter::repeat('[').take(127).chain(iter::repeat(']').take(127)).collect();
let _: Value = serde_json::from_str(&brackets).unwrap();

let brackets: String = iter::repeat('[').take(128).collect();
test_parse_err::<Value>(vec![
(&brackets, Error::Syntax(ErrorCode::Custom("recursion limit exceeded".into()), 1, 128)),
]);
}

0 comments on commit 3516a90

Please sign in to comment.