Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add from_reader_with_recursion_limit #84

Merged
merged 1 commit into from
May 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions ciborium/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,3 +831,27 @@ where

T::deserialize(&mut reader)
}

/// Deserializes as CBOR from a type with [`impl ciborium_io::Read`](ciborium_io::Read), with
/// a specified maximum recursion limit. Inputs that are nested beyond the specified limit
/// will result in [`Error::RecursionLimitExceeded`] .
///
/// Set a high recursion limit at your own risk (of stack exhaustion)!
#[inline]
pub fn from_reader_with_recursion_limit<T: de::DeserializeOwned, R: Read>(
reader: R,
recurse_limit: usize,
) -> Result<T, Error<R::Error>>
where
R::Error: core::fmt::Debug,
{
let mut scratch = [0; 4096];

let mut reader = Deserializer {
decoder: reader.into(),
scratch: &mut scratch,
recurse: recurse_limit,
};

T::deserialize(&mut reader)
}
46 changes: 45 additions & 1 deletion ciborium/tests/recursion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! test each of these types here to ensure there is no stack overflow.

use ciborium::{
de::{from_reader, Error},
de::{from_reader, from_reader_with_recursion_limit, Error},
value::Value,
};

Expand Down Expand Up @@ -46,3 +46,47 @@ fn text() {
e => panic!("incorrect error: {:?}", e),
}
}

#[test]
fn array_limit() {
let bytes = [0x9f; 128 * 1024];
for limit in 16..256 {
match from_reader_with_recursion_limit::<Value, _>(&bytes[..], limit).unwrap_err() {
Error::RecursionLimitExceeded => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
// Data that is nested beyond the limit should fail with `RecursionLimitExceeded`
match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit + 1], limit).unwrap_err()
{
Error::RecursionLimitExceeded => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
// Data that is nested within the limit fails with a different error.
match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit], limit).unwrap_err() {
Error::Io(..) => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
}
}

#[test]
fn map_limit() {
let bytes = [0xbf; 128 * 1024];
for limit in 16..256 {
match from_reader_with_recursion_limit::<Value, _>(&bytes[..], limit).unwrap_err() {
Error::RecursionLimitExceeded => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
// Data that is nested beyond the limit should fail with `RecursionLimitExceeded`
match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit + 1], limit).unwrap_err()
{
Error::RecursionLimitExceeded => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
// Data that is nested within the limit fails with a different error.
match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit], limit).unwrap_err() {
Error::Io(..) => (),
e => panic!("incorrect error with limit {}: {:?}", limit, e),
}
}
}