Skip to content

Commit

Permalink
Prevent blowup in recursive types
Browse files Browse the repository at this point in the history
  • Loading branch information
chmp committed Oct 31, 2023
1 parent 7c91f3f commit 6686f46
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 0 deletions.
26 changes: 26 additions & 0 deletions serde_arrow/src/internal/tracing/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ use crate::internal::{
tracing::TracingOptions,
};

// TODO: allow to customize
const MAX_TYPE_DEPTH: usize = 20;
const RECURSIVE_TYPE_WARNING: &str =
"too deeply nested type detected. Recursive types are not supported in schema tracing";

macro_rules! defined_tracer {
($($variant:ident($impl:ident)),* $(,)? ) => {
#[derive(Debug, PartialEq, Clone)]
Expand Down Expand Up @@ -96,6 +101,10 @@ impl Tracer {
pub fn reset(&mut self) -> Result<()> {
dispatch_tracer!(self, tracer => tracer.reset())
}

pub fn get_depth(&self) -> usize {
self.get_path().chars().filter(|c| *c == '.').count()
}
}

// TODO: move into trace any?
Expand All @@ -104,7 +113,16 @@ impl Tracer {
dispatch_tracer!(self, tracer => { tracer.nullable = true; });
}

pub fn enforce_depth_limit(&self) -> Result<()> {
if self.get_depth() >= MAX_TYPE_DEPTH {
fail!("{RECURSIVE_TYPE_WARNING}");
}
Ok(())
}

pub fn ensure_struct<S: std::fmt::Display>(&mut self, fields: &[S]) -> Result<()> {
self.enforce_depth_limit()?;

match self {
this @ Self::Unknown(_) => {
let field_names = fields
Expand Down Expand Up @@ -152,6 +170,8 @@ impl Tracer {
}

pub fn ensure_tuple(&mut self, num_fields: usize) -> Result<()> {
self.enforce_depth_limit()?;

match self {
this @ Self::Unknown(_) => {
let tracer = TupleTracer {
Expand Down Expand Up @@ -183,6 +203,8 @@ impl Tracer {
}

pub fn ensure_union(&mut self, variants: &[&str]) -> Result<()> {
self.enforce_depth_limit()?;

match self {
this @ Self::Unknown(_) => {
let tracer = UnionTracer {
Expand Down Expand Up @@ -218,6 +240,8 @@ impl Tracer {
}

pub fn ensure_list(&mut self) -> Result<()> {
self.enforce_depth_limit()?;

match self {
this @ Self::Unknown(_) => {
let tracer = ListTracer {
Expand All @@ -242,6 +266,8 @@ impl Tracer {
}

pub fn ensure_map(&mut self) -> Result<()> {
self.enforce_depth_limit()?;

match self {
this @ Self::Unknown(_) => {
let tracer = MapTracer {
Expand Down
14 changes: 14 additions & 0 deletions serde_arrow/src/test_impls/issue_90_type_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use super::macros::expect_error;
use crate::internal::{
generic::{Item, Items},
schema::{GenericDataType as T, GenericField as F, Strategy},
Expand Down Expand Up @@ -307,3 +308,16 @@ mod mixed_tracing_unions {
assert_eq!(actual, expected);
}
}

#[test]
fn unsupported_recursive_types() {
#[derive(Deserialize)]
struct Tree {
left: Option<Box<Tree>>,
right: Option<Box<Tree>>,
}

let mut tracer = Tracer::new(String::from("$"), TracingOptions::default());
let res = tracer.trace_type::<Tree>();
expect_error(&res, "too deeply nested type detected");
}

0 comments on commit 6686f46

Please sign in to comment.