From 3113f997c82e4107104b8fd88ebe2907d9326c2a Mon Sep 17 00:00:00 2001 From: Jay Geng Date: Fri, 14 Jul 2023 13:04:33 -0400 Subject: [PATCH] Add `DepthLimiter` and adapt to the Read/WriteXDR (#158) * Add `DepthLimiter` and adapt to the Read/WriteXDR * Remove external depth_limit dependency * Use global constant default depth limit * Revert "Use global constant default depth limit" This reverts commit 6a6a6a4bcee32816a29e0a53fb785dc0ff27dd10. * Use pub (non-static) const DEFAULT_MAX_DEPTH_LIMIT * fixup! Use pub (non-static) const DEFAULT_MAX_DEPTH_LIMIT * Update test files * Add comments * fixup! Add comments * fixup! Add comments * Rename `depth` for clarity; add methods for user-passed depth limit * Rename the constant for better clarity * fixup! Rename the constant for better clarity * fixup! Rename the constant for better clarity * fixup! Rename the constant for better clarity * Remove `RefCell`; make `leave()` fallible --- lib/xdrgen/generators/rust.rb | 148 +- lib/xdrgen/generators/rust/Cargo.lock | 63 + lib/xdrgen/generators/rust/Cargo.toml | 6 +- lib/xdrgen/generators/rust/src/types.rs | 749 +++++++--- .../block_comments.x/MyXDR.rs | 799 +++++++---- .../generator_spec_rust/const.x/MyXDR.rs | 789 ++++++---- .../generator_spec_rust/enum.x/MyXDR.rs | 851 +++++++---- .../generator_spec_rust/nesting.x/MyXDR.rs | 899 ++++++++---- .../generator_spec_rust/optional.x/MyXDR.rs | 805 +++++++---- .../generator_spec_rust/struct.x/MyXDR.rs | 805 +++++++---- .../generator_spec_rust/test.x/MyXDR.rs | 1263 +++++++++++------ .../generator_spec_rust/union.x/MyXDR.rs | 925 ++++++++---- 12 files changed, 5499 insertions(+), 2603 deletions(-) diff --git a/lib/xdrgen/generators/rust.rb b/lib/xdrgen/generators/rust.rb index 225686b71..e388b156c 100644 --- a/lib/xdrgen/generators/rust.rb +++ b/lib/xdrgen/generators/rust.rb @@ -166,21 +166,21 @@ def render_enum_of_all_types(out, types) #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - #{types.map { |t| "TypeVariant::#{t} => Ok(Self::#{t}(Box::new(#{t}::read_xdr(r)?)))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => r.with_limited_depth(|r| Ok(Self::#{t}(Box::new(#{t}::read_xdr(r)?))))," }.join("\n")} } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -192,40 +192,40 @@ def render_enum_of_all_types(out, types) } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(r).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, Frame<#{t}>>::new(r).map(|r| r.map(|t| Self::#{t}(Box::new(t.0)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, Frame<#{t}>>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t.0)))))," }.join("\n")} } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(dec).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} + #{types.map { |t| "TypeVariant::#{t} => Box::new(ReadXdrIter::<_, #{t}>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::#{t}(Box::new(t)))))," }.join("\n")} } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -233,7 +233,7 @@ def render_enum_of_all_types(out, types) #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } @@ -356,22 +356,26 @@ def render_struct(out, struct) out.puts <<-EOS.strip_heredoc impl ReadXdr for #{name struct} { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - #{struct.members.map do |m| - "#{field_name(m)}: #{reference_to_call(struct, m.declaration.type)}::read_xdr(r)?," - end.join("\n")} + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + #{struct.members.map do |m| + "#{field_name(m)}: #{reference_to_call(struct, m.declaration.type)}::read_xdr(r)?," + end.join("\n")} + }) }) } } impl WriteXdr for #{name struct} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - #{struct.members.map do |m| - "self.#{field_name(m)}.write_xdr(w)?;" - end.join("\n")} - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + #{struct.members.map do |m| + "self.#{field_name(m)}.write_xdr(w)?;" + end.join("\n")} + Ok(()) + }) } } EOS @@ -455,18 +459,22 @@ def render_enum(out, enum) impl ReadXdr for #{name enum} { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for #{name enum} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } EOS @@ -583,39 +591,43 @@ def render_union(out, union) impl ReadXdr for #{name union} { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let dv: #{discriminant_type} = <#{discriminant_type} as ReadXdr>::read_xdr(r)?; - #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - let v = match dv { - #{union_cases(union) do |case_name, arm, value| - "#{ - value.nil? ? "#{discriminant_type}::#{case_name}" : "#{value}" - } => #{ - arm.void? ? "Self::#{case_name}" : "Self::#{case_name}(#{reference_to_call(union, arm.type)}::read_xdr(r)?)" - }," - end.join("\n")} - #[allow(unreachable_patterns)] - _ => return Err(Error::Invalid), - }; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let dv: #{discriminant_type} = <#{discriminant_type} as ReadXdr>::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + #{union_cases(union) do |case_name, arm, value| + "#{ + value.nil? ? "#{discriminant_type}::#{case_name}" : "#{value}" + } => #{ + arm.void? ? "Self::#{case_name}" : "Self::#{case_name}(#{reference_to_call(union, arm.type)}::read_xdr(r)?)" + }," + end.join("\n")} + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) } } impl WriteXdr for #{name union} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.discriminant().write_xdr(w)?; - #[allow(clippy::match_same_arms)] - match self { - #{union_cases(union) do |case_name, arm, value| - if arm.void? - "Self::#{case_name} => ().write_xdr(w)?," - else - "Self::#{case_name}(v) => v.write_xdr(w)?," - end - end.join("\n")} - }; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + #{union_cases(union) do |case_name, arm, value| + if arm.void? + "Self::#{case_name} => ().write_xdr(w)?," + else + "Self::#{case_name}(v) => v.write_xdr(w)?," + end + end.join("\n")} + }; + Ok(()) + }) } } EOS @@ -694,17 +706,19 @@ def render_typedef(out, typedef) impl ReadXdr for #{name typedef} { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = #{reference_to_call(typedef, typedef.type)}::read_xdr(r)?; - let v = #{name typedef}(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = #{reference_to_call(typedef, typedef.type)}::read_xdr(r)?; + let v = #{name typedef}(i); + Ok(v) + }) } } impl WriteXdr for #{name typedef} { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } EOS diff --git a/lib/xdrgen/generators/rust/Cargo.lock b/lib/xdrgen/generators/rust/Cargo.lock index 33875d068..ad99ce2e9 100644 --- a/lib/xdrgen/generators/rust/Cargo.lock +++ b/lib/xdrgen/generators/rust/Cargo.lock @@ -2,15 +2,78 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "derive_arbitrary" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rs-xdr-types" version = "0.0.0" dependencies = [ + "arbitrary", "base64", + "hex", ] + +[[package]] +name = "syn" +version = "2.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" diff --git a/lib/xdrgen/generators/rust/Cargo.toml b/lib/xdrgen/generators/rust/Cargo.toml index 8a72e6686..df209f287 100644 --- a/lib/xdrgen/generators/rust/Cargo.toml +++ b/lib/xdrgen/generators/rust/Cargo.toml @@ -4,10 +4,12 @@ version = "0.0.0" edition = "2021" [dependencies] -base64 = "0.13.0" +base64 = { version = "0.13.0", optional = true } arbitrary = { version = "1.1.3", features = ["derive"] } +hex = { version = "0.4.3", optional = true } [features] default = ["std"] std = ["alloc", "base64/std"] -alloc = [] +alloc = ["dep:hex"] +base64 = ["std", "dep:base64"] diff --git a/lib/xdrgen/generators/rust/src/types.rs b/lib/xdrgen/generators/rust/src/types.rs index 676081d78..66c78bf76 100644 --- a/lib/xdrgen/generators/rust/src/types.rs +++ b/lib/xdrgen/generators/rust/src/types.rs @@ -41,6 +41,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -56,6 +64,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -99,6 +108,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -170,17 +180,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -204,7 +370,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -214,7 +380,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -237,18 +404,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -272,7 +442,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -288,8 +458,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -306,10 +479,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -333,7 +506,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -363,63 +536,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -431,229 +638,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -980,68 +1198,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1370,36 +1596,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1749,36 +1979,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1799,7 +2033,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1822,26 +2056,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1850,8 +2092,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1862,7 +2105,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1870,28 +2118,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1900,8 +2154,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1912,7 +2166,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1920,14 +2177,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1952,6 +2214,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] diff --git a/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs b/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs index 53837846b..be37db63c 100644 --- a/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/block_comments.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2057,18 +2358,22 @@ impl From for i32 { impl ReadXdr for AccountFlags { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for AccountFlags { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2142,21 +2447,21 @@ impl Type { #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::AccountFlags => Ok(Self::AccountFlags(Box::new(AccountFlags::read_xdr(r)?))), + TypeVariant::AccountFlags => r.with_limited_depth(|r| Ok(Self::AccountFlags(Box::new(AccountFlags::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2168,40 +2473,40 @@ impl Type { } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(r).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::AccountFlags(Box::new(t.0))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(dec).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), + TypeVariant::AccountFlags => Box::new(ReadXdrIter::<_, AccountFlags>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::AccountFlags(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2209,7 +2514,7 @@ impl Type { #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/const.x/MyXDR.rs b/spec/output/generator_spec_rust/const.x/MyXDR.rs index 3dcd91c9e..0f05ac124 100644 --- a/spec/output/generator_spec_rust/const.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/const.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2077,22 +2378,22 @@ TypeVariant::TestArray2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::TestArray => Ok(Self::TestArray(Box::new(TestArray::read_xdr(r)?))), -TypeVariant::TestArray2 => Ok(Self::TestArray2(Box::new(TestArray2::read_xdr(r)?))), + TypeVariant::TestArray => r.with_limited_depth(|r| Ok(Self::TestArray(Box::new(TestArray::read_xdr(r)?)))), +TypeVariant::TestArray2 => r.with_limited_depth(|r| Ok(Self::TestArray2(Box::new(TestArray2::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2104,43 +2405,43 @@ TypeVariant::TestArray2 => Ok(Self::TestArray2(Box::new(TestArray2::read_xdr(r)? } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(r).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(r).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::TestArray(Box::new(t.0))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::TestArray2(Box::new(t.0))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t.0))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(dec).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), -TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), + TypeVariant::TestArray => Box::new(ReadXdrIter::<_, TestArray>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::TestArray(Box::new(t))))), +TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::TestArray2(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2148,7 +2449,7 @@ TypeVariant::TestArray2 => Box::new(ReadXdrIter::<_, TestArray2>::new(dec).map(| #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/enum.x/MyXDR.rs b/spec/output/generator_spec_rust/enum.x/MyXDR.rs index 844aca01e..431adc33c 100644 --- a/spec/output/generator_spec_rust/enum.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/enum.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2141,18 +2442,22 @@ Self::FbaMessage => "FbaMessage", impl ReadXdr for MessageType { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for MessageType { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2243,18 +2548,22 @@ Self::Blue => "Blue", impl ReadXdr for Color { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for Color { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2345,18 +2654,22 @@ Self::Blue2 => "Blue2", impl ReadXdr for Color2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for Color2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2446,23 +2759,23 @@ TypeVariant::Color2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::MessageType => Ok(Self::MessageType(Box::new(MessageType::read_xdr(r)?))), -TypeVariant::Color => Ok(Self::Color(Box::new(Color::read_xdr(r)?))), -TypeVariant::Color2 => Ok(Self::Color2(Box::new(Color2::read_xdr(r)?))), + TypeVariant::MessageType => r.with_limited_depth(|r| Ok(Self::MessageType(Box::new(MessageType::read_xdr(r)?)))), +TypeVariant::Color => r.with_limited_depth(|r| Ok(Self::Color(Box::new(Color::read_xdr(r)?)))), +TypeVariant::Color2 => r.with_limited_depth(|r| Ok(Self::Color2(Box::new(Color2::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2474,46 +2787,46 @@ TypeVariant::Color2 => Ok(Self::Color2(Box::new(Color2::read_xdr(r)?))), } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(r).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(r).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(r).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MessageType(Box::new(t.0))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Color2(Box::new(t.0))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(dec).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec).map(|r| r.map(|t| Self::Color2(Box::new(t))))), + TypeVariant::MessageType => Box::new(ReadXdrIter::<_, MessageType>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MessageType(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color2(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2521,7 +2834,7 @@ TypeVariant::Color2 => Box::new(ReadXdrIter::<_, Color2>::new(dec).map(|r| r.map #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/nesting.x/MyXDR.rs b/spec/output/generator_spec_rust/nesting.x/MyXDR.rs index 9b66d3d29..c8c2078c2 100644 --- a/spec/output/generator_spec_rust/nesting.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/nesting.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2068,18 +2369,22 @@ Self::Offer => "Offer", impl ReadXdr for UnionKey { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for UnionKey { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2104,18 +2409,22 @@ pub struct MyUnionOne { impl ReadXdr for MyUnionOne { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - some_int: i32::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, + }) }) } } impl WriteXdr for MyUnionOne { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.some_int.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; + Ok(()) + }) } } @@ -2136,20 +2445,24 @@ pub struct MyUnionTwo { impl ReadXdr for MyUnionTwo { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - some_int: i32::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, foo: i32::read_xdr(r)?, + }) }) } } impl WriteXdr for MyUnionTwo { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.some_int.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; self.foo.write_xdr(w)?; - Ok(()) + Ok(()) + }) } } @@ -2244,31 +2557,35 @@ Self::Offer => UnionKey::Offer, impl ReadXdr for MyUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let dv: UnionKey = ::read_xdr(r)?; - #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - let v = match dv { - UnionKey::One => Self::One(MyUnionOne::read_xdr(r)?), + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let dv: UnionKey = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + UnionKey::One => Self::One(MyUnionOne::read_xdr(r)?), UnionKey::Two => Self::Two(MyUnionTwo::read_xdr(r)?), UnionKey::Offer => Self::Offer, - #[allow(unreachable_patterns)] - _ => return Err(Error::Invalid), - }; - Ok(v) + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) } } impl WriteXdr for MyUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.discriminant().write_xdr(w)?; - #[allow(clippy::match_same_arms)] - match self { - Self::One(v) => v.write_xdr(w)?, + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::One(v) => v.write_xdr(w)?, Self::Two(v) => v.write_xdr(w)?, Self::Offer => ().write_xdr(w)?, - }; - Ok(()) + }; + Ok(()) + }) } } @@ -2374,25 +2691,25 @@ TypeVariant::MyUnionTwo, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?))), -TypeVariant::Foo => Ok(Self::Foo(Box::new(Foo::read_xdr(r)?))), -TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?))), -TypeVariant::MyUnionOne => Ok(Self::MyUnionOne(Box::new(MyUnionOne::read_xdr(r)?))), -TypeVariant::MyUnionTwo => Ok(Self::MyUnionTwo(Box::new(MyUnionTwo::read_xdr(r)?))), + TypeVariant::UnionKey => r.with_limited_depth(|r| Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?)))), +TypeVariant::Foo => r.with_limited_depth(|r| Ok(Self::Foo(Box::new(Foo::read_xdr(r)?)))), +TypeVariant::MyUnion => r.with_limited_depth(|r| Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?)))), +TypeVariant::MyUnionOne => r.with_limited_depth(|r| Ok(Self::MyUnionOne(Box::new(MyUnionOne::read_xdr(r)?)))), +TypeVariant::MyUnionTwo => r.with_limited_depth(|r| Ok(Self::MyUnionTwo(Box::new(MyUnionTwo::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2404,52 +2721,52 @@ TypeVariant::MyUnionTwo => Ok(Self::MyUnionTwo(Box::new(MyUnionTwo::read_xdr(r)? } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(r).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(r).map(|r| r.map(|t| Self::Foo(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(r).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(r).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(r).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Foo(Box::new(t.0))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t.0))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t.0))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t.0))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(dec).map(|r| r.map(|t| Self::Foo(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(dec).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), -TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), + TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::Foo => Box::new(ReadXdrIter::<_, Foo>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Foo(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::MyUnionOne => Box::new(ReadXdrIter::<_, MyUnionOne>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionOne(Box::new(t))))), +TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnionTwo(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2457,7 +2774,7 @@ TypeVariant::MyUnionTwo => Box::new(ReadXdrIter::<_, MyUnionTwo>::new(dec).map(| #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/optional.x/MyXDR.rs b/spec/output/generator_spec_rust/optional.x/MyXDR.rs index 25460eef0..a2fcf0d4a 100644 --- a/spec/output/generator_spec_rust/optional.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/optional.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2007,22 +2308,26 @@ pub struct HasOptions { impl ReadXdr for HasOptions { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - first_option: Option::::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + first_option: Option::::read_xdr(r)?, second_option: Option::::read_xdr(r)?, third_option: Option::::read_xdr(r)?, + }) }) } } impl WriteXdr for HasOptions { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.first_option.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.first_option.write_xdr(w)?; self.second_option.write_xdr(w)?; self.third_option.write_xdr(w)?; - Ok(()) + Ok(()) + }) } } @@ -2104,22 +2409,22 @@ TypeVariant::HasOptions, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::Arr => Ok(Self::Arr(Box::new(Arr::read_xdr(r)?))), -TypeVariant::HasOptions => Ok(Self::HasOptions(Box::new(HasOptions::read_xdr(r)?))), + TypeVariant::Arr => r.with_limited_depth(|r| Ok(Self::Arr(Box::new(Arr::read_xdr(r)?)))), +TypeVariant::HasOptions => r.with_limited_depth(|r| Ok(Self::HasOptions(Box::new(HasOptions::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2131,43 +2436,43 @@ TypeVariant::HasOptions => Ok(Self::HasOptions(Box::new(HasOptions::read_xdr(r)? } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(r).map(|r| r.map(|t| Self::Arr(Box::new(t))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(r).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Arr(Box::new(t.0))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::HasOptions(Box::new(t.0))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t.0))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(dec).map(|r| r.map(|t| Self::Arr(Box::new(t))))), -TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), + TypeVariant::Arr => Box::new(ReadXdrIter::<_, Arr>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Arr(Box::new(t))))), +TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::HasOptions(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2175,7 +2480,7 @@ TypeVariant::HasOptions => Box::new(ReadXdrIter::<_, HasOptions>::new(dec).map(| #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/struct.x/MyXDR.rs b/spec/output/generator_spec_rust/struct.x/MyXDR.rs index 1421070a1..420558f31 100644 --- a/spec/output/generator_spec_rust/struct.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/struct.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2011,26 +2312,30 @@ pub struct MyStruct { impl ReadXdr for MyStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - some_int: i32::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + some_int: i32::read_xdr(r)?, a_big_int: i64::read_xdr(r)?, some_opaque: <[u8; 10]>::read_xdr(r)?, some_string: StringM::read_xdr(r)?, max_string: StringM::<100>::read_xdr(r)?, + }) }) } } impl WriteXdr for MyStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.some_int.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.some_int.write_xdr(w)?; self.a_big_int.write_xdr(w)?; self.some_opaque.write_xdr(w)?; self.some_string.write_xdr(w)?; self.max_string.write_xdr(w)?; - Ok(()) + Ok(()) + }) } } @@ -2112,22 +2417,22 @@ TypeVariant::MyStruct, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::Int64 => Ok(Self::Int64(Box::new(Int64::read_xdr(r)?))), -TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?))), + TypeVariant::Int64 => r.with_limited_depth(|r| Ok(Self::Int64(Box::new(Int64::read_xdr(r)?)))), +TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2139,43 +2444,43 @@ TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?))), } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(r).map(|r| r.map(|t| Self::Int64(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(r).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Int64(Box::new(t.0))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(dec).map(|r| r.map(|t| Self::Int64(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), + TypeVariant::Int64 => Box::new(ReadXdrIter::<_, Int64>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int64(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2183,7 +2488,7 @@ TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec).map(|r| r #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/test.x/MyXDR.rs b/spec/output/generator_spec_rust/test.x/MyXDR.rs index 5fa33b96b..f9d193f40 100644 --- a/spec/output/generator_spec_rust/test.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/test.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2042,17 +2343,19 @@ impl AsRef<[u8; 64]> for Uint512 { impl ReadXdr for Uint512 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = <[u8; 64]>::read_xdr(r)?; - let v = Uint512(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = <[u8; 64]>::read_xdr(r)?; + let v = Uint512(i); + Ok(v) + }) } } impl WriteXdr for Uint512 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2127,17 +2430,19 @@ impl AsRef> for Uint513 { impl ReadXdr for Uint513 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = BytesM::<64>::read_xdr(r)?; - let v = Uint513(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = BytesM::<64>::read_xdr(r)?; + let v = Uint513(i); + Ok(v) + }) } } impl WriteXdr for Uint513 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2224,17 +2529,19 @@ impl AsRef for Uint514 { impl ReadXdr for Uint514 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = BytesM::read_xdr(r)?; - let v = Uint514(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = BytesM::read_xdr(r)?; + let v = Uint514(i); + Ok(v) + }) } } impl WriteXdr for Uint514 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2321,17 +2628,19 @@ impl AsRef> for Str { impl ReadXdr for Str { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = StringM::<64>::read_xdr(r)?; - let v = Str(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = StringM::<64>::read_xdr(r)?; + let v = Str(i); + Ok(v) + }) } } impl WriteXdr for Str { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2418,17 +2727,19 @@ impl AsRef for Str2 { impl ReadXdr for Str2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = StringM::read_xdr(r)?; - let v = Str2(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = StringM::read_xdr(r)?; + let v = Str2(i); + Ok(v) + }) } } impl WriteXdr for Str2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2542,17 +2853,19 @@ impl AsRef<[u8; 32]> for Hash { impl ReadXdr for Hash { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = <[u8; 32]>::read_xdr(r)?; - let v = Hash(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = <[u8; 32]>::read_xdr(r)?; + let v = Hash(i); + Ok(v) + }) } } impl WriteXdr for Hash { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2626,17 +2939,19 @@ impl AsRef<[Hash; 12]> for Hashes1 { impl ReadXdr for Hashes1 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = <[Hash; 12]>::read_xdr(r)?; - let v = Hashes1(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = <[Hash; 12]>::read_xdr(r)?; + let v = Hashes1(i); + Ok(v) + }) } } impl WriteXdr for Hashes1 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2711,17 +3026,19 @@ impl AsRef> for Hashes2 { impl ReadXdr for Hashes2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = VecM::::read_xdr(r)?; - let v = Hashes2(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = VecM::::read_xdr(r)?; + let v = Hashes2(i); + Ok(v) + }) } } impl WriteXdr for Hashes2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2808,17 +3125,19 @@ impl AsRef> for Hashes3 { impl ReadXdr for Hashes3 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = VecM::::read_xdr(r)?; - let v = Hashes3(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = VecM::::read_xdr(r)?; + let v = Hashes3(i); + Ok(v) + }) } } impl WriteXdr for Hashes3 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2904,17 +3223,19 @@ impl AsRef> for OptHash1 { impl ReadXdr for OptHash1 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = Option::::read_xdr(r)?; - let v = OptHash1(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = Option::::read_xdr(r)?; + let v = OptHash1(i); + Ok(v) + }) } } impl WriteXdr for OptHash1 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2951,17 +3272,19 @@ impl AsRef> for OptHash2 { impl ReadXdr for OptHash2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = Option::::read_xdr(r)?; - let v = OptHash2(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = Option::::read_xdr(r)?; + let v = OptHash2(i); + Ok(v) + }) } } impl WriteXdr for OptHash2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -3017,30 +3340,34 @@ pub struct MyStruct { impl ReadXdr for MyStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - field1: Uint512::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + field1: Uint512::read_xdr(r)?, field2: OptHash1::read_xdr(r)?, field3: i32::read_xdr(r)?, field4: u32::read_xdr(r)?, field5: f32::read_xdr(r)?, field6: f64::read_xdr(r)?, field7: bool::read_xdr(r)?, + }) }) } } impl WriteXdr for MyStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.field1.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.field1.write_xdr(w)?; self.field2.write_xdr(w)?; self.field3.write_xdr(w)?; self.field4.write_xdr(w)?; self.field5.write_xdr(w)?; self.field6.write_xdr(w)?; self.field7.write_xdr(w)?; - Ok(()) + Ok(()) + }) } } @@ -3060,18 +3387,22 @@ pub struct LotsOfMyStructs { impl ReadXdr for LotsOfMyStructs { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - members: VecM::::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + members: VecM::::read_xdr(r)?, + }) }) } } impl WriteXdr for LotsOfMyStructs { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.members.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.members.write_xdr(w)?; + Ok(()) + }) } } @@ -3091,18 +3422,22 @@ pub struct HasStuff { impl ReadXdr for HasStuff { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - data: LotsOfMyStructs::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + data: LotsOfMyStructs::read_xdr(r)?, + }) }) } } impl WriteXdr for HasStuff { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.data.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.data.write_xdr(w)?; + Ok(()) + }) } } @@ -3193,18 +3528,22 @@ Self::Green => "Green", impl ReadXdr for Color { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for Color { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -3301,18 +3640,22 @@ Self::2 => "2", impl ReadXdr for NesterNestedEnum { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for NesterNestedEnum { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -3331,18 +3674,22 @@ pub struct NesterNestedStruct { impl ReadXdr for NesterNestedStruct { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - blah: i32::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + blah: i32::read_xdr(r)?, + }) }) } } impl WriteXdr for NesterNestedStruct { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.blah.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.blah.write_xdr(w)?; + Ok(()) + }) } } @@ -3417,27 +3764,31 @@ impl Union for NesterNestedUnion {} impl ReadXdr for NesterNestedUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let dv: Color = ::read_xdr(r)?; - #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - let v = match dv { - Color::Red => Self::Red, - #[allow(unreachable_patterns)] - _ => return Err(Error::Invalid), - }; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let dv: Color = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + Color::Red => Self::Red, + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) } } impl WriteXdr for NesterNestedUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.discriminant().write_xdr(w)?; - #[allow(clippy::match_same_arms)] - match self { - Self::Red => ().write_xdr(w)?, - }; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::Red => ().write_xdr(w)?, + }; + Ok(()) + }) } } @@ -3475,22 +3826,26 @@ pub struct Nester { impl ReadXdr for Nester { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - Ok(Self{ - nested_enum: NesterNestedEnum::read_xdr(r)?, + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + Ok(Self{ + nested_enum: NesterNestedEnum::read_xdr(r)?, nested_struct: NesterNestedStruct::read_xdr(r)?, nested_union: NesterNestedUnion::read_xdr(r)?, + }) }) } } impl WriteXdr for Nester { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.nested_enum.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.nested_enum.write_xdr(w)?; self.nested_struct.write_xdr(w)?; self.nested_union.write_xdr(w)?; - Ok(()) + Ok(()) + }) } } @@ -3740,43 +4095,43 @@ TypeVariant::NesterNestedUnion, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::Uint512 => Ok(Self::Uint512(Box::new(Uint512::read_xdr(r)?))), -TypeVariant::Uint513 => Ok(Self::Uint513(Box::new(Uint513::read_xdr(r)?))), -TypeVariant::Uint514 => Ok(Self::Uint514(Box::new(Uint514::read_xdr(r)?))), -TypeVariant::Str => Ok(Self::Str(Box::new(Str::read_xdr(r)?))), -TypeVariant::Str2 => Ok(Self::Str2(Box::new(Str2::read_xdr(r)?))), -TypeVariant::Hash => Ok(Self::Hash(Box::new(Hash::read_xdr(r)?))), -TypeVariant::Hashes1 => Ok(Self::Hashes1(Box::new(Hashes1::read_xdr(r)?))), -TypeVariant::Hashes2 => Ok(Self::Hashes2(Box::new(Hashes2::read_xdr(r)?))), -TypeVariant::Hashes3 => Ok(Self::Hashes3(Box::new(Hashes3::read_xdr(r)?))), -TypeVariant::OptHash1 => Ok(Self::OptHash1(Box::new(OptHash1::read_xdr(r)?))), -TypeVariant::OptHash2 => Ok(Self::OptHash2(Box::new(OptHash2::read_xdr(r)?))), -TypeVariant::Int1 => Ok(Self::Int1(Box::new(Int1::read_xdr(r)?))), -TypeVariant::Int2 => Ok(Self::Int2(Box::new(Int2::read_xdr(r)?))), -TypeVariant::Int3 => Ok(Self::Int3(Box::new(Int3::read_xdr(r)?))), -TypeVariant::Int4 => Ok(Self::Int4(Box::new(Int4::read_xdr(r)?))), -TypeVariant::MyStruct => Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?))), -TypeVariant::LotsOfMyStructs => Ok(Self::LotsOfMyStructs(Box::new(LotsOfMyStructs::read_xdr(r)?))), -TypeVariant::HasStuff => Ok(Self::HasStuff(Box::new(HasStuff::read_xdr(r)?))), -TypeVariant::Color => Ok(Self::Color(Box::new(Color::read_xdr(r)?))), -TypeVariant::Nester => Ok(Self::Nester(Box::new(Nester::read_xdr(r)?))), -TypeVariant::NesterNestedEnum => Ok(Self::NesterNestedEnum(Box::new(NesterNestedEnum::read_xdr(r)?))), -TypeVariant::NesterNestedStruct => Ok(Self::NesterNestedStruct(Box::new(NesterNestedStruct::read_xdr(r)?))), -TypeVariant::NesterNestedUnion => Ok(Self::NesterNestedUnion(Box::new(NesterNestedUnion::read_xdr(r)?))), + TypeVariant::Uint512 => r.with_limited_depth(|r| Ok(Self::Uint512(Box::new(Uint512::read_xdr(r)?)))), +TypeVariant::Uint513 => r.with_limited_depth(|r| Ok(Self::Uint513(Box::new(Uint513::read_xdr(r)?)))), +TypeVariant::Uint514 => r.with_limited_depth(|r| Ok(Self::Uint514(Box::new(Uint514::read_xdr(r)?)))), +TypeVariant::Str => r.with_limited_depth(|r| Ok(Self::Str(Box::new(Str::read_xdr(r)?)))), +TypeVariant::Str2 => r.with_limited_depth(|r| Ok(Self::Str2(Box::new(Str2::read_xdr(r)?)))), +TypeVariant::Hash => r.with_limited_depth(|r| Ok(Self::Hash(Box::new(Hash::read_xdr(r)?)))), +TypeVariant::Hashes1 => r.with_limited_depth(|r| Ok(Self::Hashes1(Box::new(Hashes1::read_xdr(r)?)))), +TypeVariant::Hashes2 => r.with_limited_depth(|r| Ok(Self::Hashes2(Box::new(Hashes2::read_xdr(r)?)))), +TypeVariant::Hashes3 => r.with_limited_depth(|r| Ok(Self::Hashes3(Box::new(Hashes3::read_xdr(r)?)))), +TypeVariant::OptHash1 => r.with_limited_depth(|r| Ok(Self::OptHash1(Box::new(OptHash1::read_xdr(r)?)))), +TypeVariant::OptHash2 => r.with_limited_depth(|r| Ok(Self::OptHash2(Box::new(OptHash2::read_xdr(r)?)))), +TypeVariant::Int1 => r.with_limited_depth(|r| Ok(Self::Int1(Box::new(Int1::read_xdr(r)?)))), +TypeVariant::Int2 => r.with_limited_depth(|r| Ok(Self::Int2(Box::new(Int2::read_xdr(r)?)))), +TypeVariant::Int3 => r.with_limited_depth(|r| Ok(Self::Int3(Box::new(Int3::read_xdr(r)?)))), +TypeVariant::Int4 => r.with_limited_depth(|r| Ok(Self::Int4(Box::new(Int4::read_xdr(r)?)))), +TypeVariant::MyStruct => r.with_limited_depth(|r| Ok(Self::MyStruct(Box::new(MyStruct::read_xdr(r)?)))), +TypeVariant::LotsOfMyStructs => r.with_limited_depth(|r| Ok(Self::LotsOfMyStructs(Box::new(LotsOfMyStructs::read_xdr(r)?)))), +TypeVariant::HasStuff => r.with_limited_depth(|r| Ok(Self::HasStuff(Box::new(HasStuff::read_xdr(r)?)))), +TypeVariant::Color => r.with_limited_depth(|r| Ok(Self::Color(Box::new(Color::read_xdr(r)?)))), +TypeVariant::Nester => r.with_limited_depth(|r| Ok(Self::Nester(Box::new(Nester::read_xdr(r)?)))), +TypeVariant::NesterNestedEnum => r.with_limited_depth(|r| Ok(Self::NesterNestedEnum(Box::new(NesterNestedEnum::read_xdr(r)?)))), +TypeVariant::NesterNestedStruct => r.with_limited_depth(|r| Ok(Self::NesterNestedStruct(Box::new(NesterNestedStruct::read_xdr(r)?)))), +TypeVariant::NesterNestedUnion => r.with_limited_depth(|r| Ok(Self::NesterNestedUnion(Box::new(NesterNestedUnion::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -3788,106 +4143,106 @@ TypeVariant::NesterNestedUnion => Ok(Self::NesterNestedUnion(Box::new(NesterNest } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(r).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(r).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(r).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(r).map(|r| r.map(|t| Self::Str(Box::new(t))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(r).map(|r| r.map(|t| Self::Str2(Box::new(t))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(r).map(|r| r.map(|t| Self::Hash(Box::new(t))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(r).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(r).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(r).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(r).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(r).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(r).map(|r| r.map(|t| Self::Int1(Box::new(t))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(r).map(|r| r.map(|t| Self::Int2(Box::new(t))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(r).map(|r| r.map(|t| Self::Int3(Box::new(t))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(r).map(|r| r.map(|t| Self::Int4(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(r).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(r).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(r).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(r).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(r).map(|r| r.map(|t| Self::Nester(Box::new(t))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(r).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(r).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(r).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Uint512(Box::new(t.0))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Uint513(Box::new(t.0))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Uint514(Box::new(t.0))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Str(Box::new(t.0))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Str2(Box::new(t.0))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Hash(Box::new(t.0))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Hashes1(Box::new(t.0))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Hashes2(Box::new(t.0))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Hashes3(Box::new(t.0))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::OptHash1(Box::new(t.0))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::OptHash2(Box::new(t.0))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Int1(Box::new(t.0))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Int2(Box::new(t.0))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Int3(Box::new(t.0))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Int4(Box::new(t.0))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t.0))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::HasStuff(Box::new(t.0))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Nester(Box::new(t.0))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t.0))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t.0))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t.0))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t.0))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t.0))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t.0))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t.0))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t.0))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t.0))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t.0))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t.0))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t.0))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t.0))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t.0))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t.0))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t.0))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t.0))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t.0))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t.0))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t.0))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t.0))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t.0))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t.0))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t.0))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t.0))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(dec).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), -TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(dec).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), -TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(dec).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), -TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(dec).map(|r| r.map(|t| Self::Str(Box::new(t))))), -TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(dec).map(|r| r.map(|t| Self::Str2(Box::new(t))))), -TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(dec).map(|r| r.map(|t| Self::Hash(Box::new(t))))), -TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(dec).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), -TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(dec).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), -TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(dec).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), -TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(dec).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), -TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(dec).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), -TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(dec).map(|r| r.map(|t| Self::Int1(Box::new(t))))), -TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(dec).map(|r| r.map(|t| Self::Int2(Box::new(t))))), -TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(dec).map(|r| r.map(|t| Self::Int3(Box::new(t))))), -TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(dec).map(|r| r.map(|t| Self::Int4(Box::new(t))))), -TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), -TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(dec).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), -TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(dec).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), -TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec).map(|r| r.map(|t| Self::Color(Box::new(t))))), -TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(dec).map(|r| r.map(|t| Self::Nester(Box::new(t))))), -TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(dec).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), -TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(dec).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), -TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(dec).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), + TypeVariant::Uint512 => Box::new(ReadXdrIter::<_, Uint512>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint512(Box::new(t))))), +TypeVariant::Uint513 => Box::new(ReadXdrIter::<_, Uint513>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint513(Box::new(t))))), +TypeVariant::Uint514 => Box::new(ReadXdrIter::<_, Uint514>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Uint514(Box::new(t))))), +TypeVariant::Str => Box::new(ReadXdrIter::<_, Str>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Str(Box::new(t))))), +TypeVariant::Str2 => Box::new(ReadXdrIter::<_, Str2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Str2(Box::new(t))))), +TypeVariant::Hash => Box::new(ReadXdrIter::<_, Hash>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hash(Box::new(t))))), +TypeVariant::Hashes1 => Box::new(ReadXdrIter::<_, Hashes1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes1(Box::new(t))))), +TypeVariant::Hashes2 => Box::new(ReadXdrIter::<_, Hashes2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes2(Box::new(t))))), +TypeVariant::Hashes3 => Box::new(ReadXdrIter::<_, Hashes3>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Hashes3(Box::new(t))))), +TypeVariant::OptHash1 => Box::new(ReadXdrIter::<_, OptHash1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::OptHash1(Box::new(t))))), +TypeVariant::OptHash2 => Box::new(ReadXdrIter::<_, OptHash2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::OptHash2(Box::new(t))))), +TypeVariant::Int1 => Box::new(ReadXdrIter::<_, Int1>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int1(Box::new(t))))), +TypeVariant::Int2 => Box::new(ReadXdrIter::<_, Int2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int2(Box::new(t))))), +TypeVariant::Int3 => Box::new(ReadXdrIter::<_, Int3>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int3(Box::new(t))))), +TypeVariant::Int4 => Box::new(ReadXdrIter::<_, Int4>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Int4(Box::new(t))))), +TypeVariant::MyStruct => Box::new(ReadXdrIter::<_, MyStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyStruct(Box::new(t))))), +TypeVariant::LotsOfMyStructs => Box::new(ReadXdrIter::<_, LotsOfMyStructs>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::LotsOfMyStructs(Box::new(t))))), +TypeVariant::HasStuff => Box::new(ReadXdrIter::<_, HasStuff>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::HasStuff(Box::new(t))))), +TypeVariant::Color => Box::new(ReadXdrIter::<_, Color>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Color(Box::new(t))))), +TypeVariant::Nester => Box::new(ReadXdrIter::<_, Nester>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Nester(Box::new(t))))), +TypeVariant::NesterNestedEnum => Box::new(ReadXdrIter::<_, NesterNestedEnum>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedEnum(Box::new(t))))), +TypeVariant::NesterNestedStruct => Box::new(ReadXdrIter::<_, NesterNestedStruct>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedStruct(Box::new(t))))), +TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::NesterNestedUnion(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -3895,7 +4250,7 @@ TypeVariant::NesterNestedUnion => Box::new(ReadXdrIter::<_, NesterNestedUnion>:: #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } diff --git a/spec/output/generator_spec_rust/union.x/MyXDR.rs b/spec/output/generator_spec_rust/union.x/MyXDR.rs index bbf28c006..8bd286e24 100644 --- a/spec/output/generator_spec_rust/union.x/MyXDR.rs +++ b/spec/output/generator_spec_rust/union.x/MyXDR.rs @@ -51,6 +51,14 @@ use std::{ io::{BufRead, BufReader, Cursor, Read, Write}, }; +/// Defines the maximum depth for recursive calls in `Read/WriteXdr` to prevent stack overflow. +/// +/// The depth limit is akin to limiting stack depth. Its purpose is to prevent the program from +/// hitting the maximum stack size allowed by Rust, which would result in an unrecoverable `SIGABRT`. +/// For more information about Rust's stack size limit, refer to the +/// [Rust documentation](https://doc.rust-lang.org/std/thread/#stack-size). +pub const DEFAULT_XDR_RW_DEPTH_LIMIT: u32 = 500; + /// Error contains all errors returned by functions in this crate. It can be /// compared via `PartialEq`, however any contained IO errors will only be /// compared on their `ErrorKind`. @@ -66,6 +74,7 @@ pub enum Error { InvalidHex, #[cfg(feature = "std")] Io(io::Error), + DepthLimitExceeded, } impl PartialEq for Error { @@ -109,6 +118,7 @@ impl fmt::Display for Error { Error::InvalidHex => write!(f, "hex invalid"), #[cfg(feature = "std")] Error::Io(e) => write!(f, "{e}"), + Error::DepthLimitExceeded => write!(f, "depth limit exceeded"), } } } @@ -180,17 +190,173 @@ where { } +/// `DepthLimiter` is a trait designed for managing the depth of recursive operations. +/// It provides a mechanism to limit recursion depth, and defines the behavior upon +/// entering and leaving a recursion level. +pub trait DepthLimiter { + /// A general error type for any type implementing, or an operation under the guard of + /// `DepthLimiter`. It must at least include the error case where the depth limit is exceeded + /// which is returned from `enter`. + type DepthLimiterError; + + /// Defines the behavior for entering a new recursion level. + /// A `DepthLimiterError` is returned if the new level exceeds the depth limit. + fn enter(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Defines the behavior for leaving a recursion level. + /// A `DepthLimiterError` is returned if an error occurs. + fn leave(&mut self) -> core::result::Result<(), Self::DepthLimiterError>; + + /// Wraps a given function `f` with depth limiting guards. + /// It triggers an `enter` before, and a `leave` after the execution of `f`. + /// + /// # Parameters + /// + /// - `f`: The function to be executed under depth limit constraints. + /// + /// # Returns + /// + /// - `Err` if 1. the depth limit has been exceeded upon `enter` 2. `f` executes + /// with an error 3. if error occurs on `leave`. + /// `Ok` otherwise. + fn with_limited_depth(&mut self, f: F) -> core::result::Result + where + F: FnOnce(&mut Self) -> core::result::Result, + { + self.enter()?; + let res = f(self); + self.leave()?; + res + } +} + +/// `DepthLimitedRead` wraps a `Read` object and enforces a depth limit to +/// recursive read operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedRead { + pub inner: R, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedRead { + /// Constructs a new `DepthLimitedRead`. + /// + /// - `inner`: The object implementing the `Read` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: R, depth_limit: u32) -> Self { + DepthLimitedRead { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedRead { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the `depth_remaining` is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> core::result::Result<(), Error> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Read for DepthLimitedRead { + /// Forwards the read operation to the wrapped object. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +/// `DepthLimitedWrite` wraps a `Write` object and enforces a depth limit to +/// recursive write operations. It maintains a `depth_remaining` state tracking +/// remaining allowed recursion depth. +#[cfg(feature = "std")] +pub struct DepthLimitedWrite { + pub inner: W, + pub(crate) depth_remaining: u32, +} + +#[cfg(feature = "std")] +impl DepthLimitedWrite { + /// Constructs a new `DepthLimitedWrite`. + /// + /// - `inner`: The object implementing the `Write` trait. + /// - `depth_limit`: The maximum allowed recursion depth. + pub fn new(inner: W, depth_limit: u32) -> Self { + DepthLimitedWrite { + inner, + depth_remaining: depth_limit, + } + } +} + +#[cfg(feature = "std")] +impl DepthLimiter for DepthLimitedWrite { + type DepthLimiterError = Error; + + /// Decrements the `depth_remaining`. If the depth is already zero, an error is + /// returned indicating that the maximum depth limit has been exceeded. + fn enter(&mut self) -> Result<()> { + if let Some(depth) = self.depth_remaining.checked_sub(1) { + self.depth_remaining = depth; + } else { + return Err(Error::DepthLimitExceeded); + } + Ok(()) + } + + /// Increments the depth. `leave` should be called in tandem with `enter` such that the depth + /// doesn't exceed the initial depth limit. + fn leave(&mut self) -> core::result::Result<(), Error> { + self.depth_remaining = self.depth_remaining.saturating_add(1); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl Write for DepthLimitedWrite { + /// Forwards the write operation to the wrapped object. + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.inner.write(buf) + } + + /// Forwards the flush operation to the wrapped object. + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + #[cfg(feature = "std")] pub struct ReadXdrIter { - reader: BufReader, + reader: DepthLimitedRead>, _s: PhantomData, } #[cfg(feature = "std")] impl ReadXdrIter { - fn new(r: R) -> Self { + fn new(r: R, depth_limit: u32) -> Self { Self { - reader: BufReader::new(r), + reader: DepthLimitedRead { + inner: BufReader::new(r), + depth_remaining: depth_limit, + }, _s: PhantomData, } } @@ -214,7 +380,7 @@ impl Iterator for ReadXdrIter { // xdr types in this crate heavily use the `std::io::Read::read_exact` // method that doesn't distinguish between an EOF at the beginning of a // read and an EOF after a partial fill of a read_exact. - match self.reader.fill_buf() { + match self.reader.inner.fill_buf() { // If the reader has no more data and is unable to fill any new data // into its internal buf, then the EOF has been reached. Ok([]) => return None, @@ -224,7 +390,8 @@ impl Iterator for ReadXdrIter { Ok([..]) => (), }; // Read the buf into the type. - match S::read_xdr(&mut self.reader) { + let r = self.reader.with_limited_depth(|dlr| S::read_xdr(dlr)); + match r { Ok(s) => Some(Ok(s)), Err(e) => Some(Err(e)), } @@ -247,18 +414,21 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_to_end`] when the intent is for all bytes in the + /// Use [`ReadXdR: Read_xdr_to_end`] when the intent is for all bytes in the /// read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result; + fn read_xdr(r: &mut DepthLimitedRead) -> Result; /// Construct the type from the XDR bytes base64 encoded. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr(&mut dec)?; Ok(t) } @@ -282,7 +452,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_to_end(r: &mut impl Read) -> Result { + fn read_xdr_to_end(r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -298,8 +468,11 @@ where /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn read_xdr_base64_to_end(r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + fn read_xdr_base64_to_end(r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), + r.depth_remaining, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } @@ -316,10 +489,10 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). /// - /// Use [`ReadXdr::read_xdr_into_to_end`] when the intent is for all bytes + /// Use [`ReadXdR: Read_xdr_into_to_end`] when the intent is for all bytes /// in the read implementation to be consumed by the read. #[cfg(feature = "std")] - fn read_xdr_into(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into(&mut self, r: &mut DepthLimitedRead) -> Result<()> { *self = Self::read_xdr(r)?; Ok(()) } @@ -343,7 +516,7 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_into_to_end(&mut self, r: &mut impl Read) -> Result<()> { + fn read_xdr_into_to_end(&mut self, r: &mut DepthLimitedRead) -> Result<()> { Self::read_xdr_into(self, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -373,63 +546,97 @@ where /// All implementations should continue if the read implementation returns /// [`ErrorKind::Interrupted`](std::io::ErrorKind::Interrupted). #[cfg(feature = "std")] - fn read_xdr_iter(r: &mut R) -> ReadXdrIter<&mut R, Self> { - ReadXdrIter::new(r) + fn read_xdr_iter(r: &mut DepthLimitedRead) -> ReadXdrIter<&mut R, Self> { + ReadXdrIter::new(&mut r.inner, r.depth_remaining) } /// Create an iterator that reads the read implementation as a stream of /// values that are read into the implementing type. #[cfg(feature = "base64")] fn read_xdr_base64_iter( - r: &mut R, + r: &mut DepthLimitedRead, ) -> ReadXdrIter, Self> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); - ReadXdrIter::new(dec) + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); + ReadXdrIter::new(dec, r.depth_remaining) } - /// Construct the type from the XDR bytes. + /// Construct the type from the XDR bytes, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "std")] - fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + fn from_xdr_with_depth_limit(bytes: impl AsRef<[u8]>, depth_limit: u32) -> Result { + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), depth_limit); let t = Self::read_xdr_to_end(&mut cursor)?; Ok(t) } - /// Construct the type from the XDR bytes base64 encoded. + /// Construct the type from the XDR bytes, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "std")] + fn from_xdr(bytes: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_with_depth_limit(bytes, DEFAULT_XDR_RW_DEPTH_LIMIT) + } + + /// Construct the type from the XDR bytes base64 encoded, specifying a depth limit. /// /// An error is returned if the bytes are not completely consumed by the /// deserialization. #[cfg(feature = "base64")] - fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + fn from_xdr_base64_with_depth_limit(b64: impl AsRef<[u8]>, depth_limit: u32) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new( + base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), + depth_limit, + ); let t = Self::read_xdr_to_end(&mut dec)?; Ok(t) } + + /// Construct the type from the XDR bytes base64 encoded, using the default depth limit. + /// + /// An error is returned if the bytes are not completely consumed by the + /// deserialization. + #[cfg(feature = "base64")] + fn from_xdr_base64(b64: impl AsRef<[u8]>) -> Result { + ReadXdr::from_xdr_base64_with_depth_limit(b64, DEFAULT_XDR_RW_DEPTH_LIMIT) + } } pub trait WriteXdr { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()>; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()>; #[cfg(feature = "std")] - fn to_xdr(&self) -> Result> { - let mut cursor = Cursor::new(vec![]); + fn to_xdr_with_depth_limit(&self, depth_limit: u32) -> Result> { + let mut cursor = DepthLimitedWrite::new(Cursor::new(vec![]), depth_limit); self.write_xdr(&mut cursor)?; - let bytes = cursor.into_inner(); + let bytes = cursor.inner.into_inner(); Ok(bytes) } + #[cfg(feature = "std")] + fn to_xdr(&self) -> Result> { + self.to_xdr_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } + #[cfg(feature = "base64")] - fn to_xdr_base64(&self) -> Result { - let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD); + fn to_xdr_base64_with_depth_limit(&self, depth_limit: u32) -> Result { + let mut enc = DepthLimitedWrite::new( + base64::write::EncoderStringWriter::new(base64::STANDARD), + depth_limit, + ); self.write_xdr(&mut enc)?; - let b64 = enc.into_inner(); + let b64 = enc.inner.into_inner(); Ok(b64) } + + #[cfg(feature = "base64")] + fn to_xdr_base64(&self) -> Result { + self.to_xdr_base64_with_depth_limit(DEFAULT_XDR_RW_DEPTH_LIMIT) + } } /// `Pad_len` returns the number of bytes to pad an XDR value of the given @@ -441,229 +648,240 @@ fn pad_len(len: usize) -> usize { impl ReadXdr for i32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = i32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i32::from_be_bytes(b)) + }) } } impl WriteXdr for i32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u32 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 4]; - r.read_exact(&mut b)?; - let i = u32::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u32::from_be_bytes(b)) + }) } } impl WriteXdr for u32 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 4] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for i64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = i64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(i64::from_be_bytes(b)) + }) } } impl WriteXdr for i64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for u64 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { let mut b = [0u8; 8]; - r.read_exact(&mut b)?; - let i = u64::from_be_bytes(b); - Ok(i) + r.with_limited_depth(|r| { + r.read_exact(&mut b)?; + Ok(u64::from_be_bytes(b)) + }) } } impl WriteXdr for u64 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { let b: [u8; 8] = self.to_be_bytes(); - w.write_all(&b)?; - Ok(()) + w.with_limited_depth(|w| Ok(w.write_all(&b)?)) } } impl ReadXdr for f32 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f32 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for f64 { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { todo!() } } impl WriteXdr for f64 { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { todo!() } } impl ReadXdr for bool { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - let b = i == 1; - Ok(b) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + let b = i == 1; + Ok(b) + }) } } impl WriteXdr for bool { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i = u32::from(*self); // true = 1, false = 0 - i.write_xdr(w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i = u32::from(*self); // true = 1, false = 0 + i.write_xdr(w) + }) } } impl ReadXdr for Option { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = u32::read_xdr(r)?; - match i { - 0 => Ok(None), - 1 => { - let t = T::read_xdr(r)?; - Ok(Some(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = u32::read_xdr(r)?; + match i { + 0 => Ok(None), + 1 => { + let t = T::read_xdr(r)?; + Ok(Some(t)) + } + _ => Err(Error::Invalid), } - _ => Err(Error::Invalid), - } + }) } } impl WriteXdr for Option { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - if let Some(t) = self { - 1u32.write_xdr(w)?; - t.write_xdr(w)?; - } else { - 0u32.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + if let Some(t) = self { + 1u32.write_xdr(w)?; + t.write_xdr(w)?; + } else { + 0u32.write_xdr(w)?; + } + Ok(()) + }) } } impl ReadXdr for Box { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let t = T::read_xdr(r)?; - Ok(Box::new(t)) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| Ok(Box::new(T::read_xdr(r)?))) } } impl WriteXdr for Box { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - T::write_xdr(self, w)?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| T::write_xdr(self, w)) } } impl ReadXdr for () { #[cfg(feature = "std")] - fn read_xdr(_r: &mut impl Read) -> Result { + fn read_xdr(_r: &mut DepthLimitedRead) -> Result { Ok(()) } } impl WriteXdr for () { #[cfg(feature = "std")] - fn write_xdr(&self, _w: &mut impl Write) -> Result<()> { + fn write_xdr(&self, _w: &mut DepthLimitedWrite) -> Result<()> { Ok(()) } } impl ReadXdr for [u8; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut arr = [0u8; N]; - r.read_exact(&mut arr)?; - - let pad = &mut [0u8; 3][..pad_len(N)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } - - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut arr = [0u8; N]; + r.read_exact(&mut arr)?; + let pad = &mut [0u8; 3][..pad_len(N)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } + Ok(arr) + }) } } impl WriteXdr for [u8; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - w.write_all(self)?; - w.write_all(&[0u8; 3][..pad_len(N)])?; - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + w.write_all(self)?; + w.write_all(&[0u8; 3][..pad_len(N)])?; + Ok(()) + }) } } impl ReadXdr for [T; N] { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let mut vec = Vec::with_capacity(N); - for _ in 0..N { - let t = T::read_xdr(r)?; - vec.push(t); - } - let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); - Ok(arr) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let mut vec = Vec::with_capacity(N); + for _ in 0..N { + let t = T::read_xdr(r)?; + vec.push(t); + } + let arr: [T; N] = vec.try_into().unwrap_or_else(|_: Vec| unreachable!()); + Ok(arr) + }) } } impl WriteXdr for [T; N] { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - for t in self { - t.write_xdr(w)?; - } - Ok(()) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + for t in self { + t.write_xdr(w)?; + } + Ok(()) + }) } } @@ -990,68 +1208,76 @@ impl<'a, const MAX: u32> TryFrom<&'a VecM> for &'a str { impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } impl ReadXdr for VecM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len { - let t = T::read_xdr(r)?; - vec.push(t); - } + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + let t = T::read_xdr(r)?; + vec.push(t); + } - Ok(VecM(vec)) + Ok(VecM(vec)) + }) } } impl WriteXdr for VecM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - for t in &self.0 { - t.write_xdr(w)?; - } + for t in &self.0 { + t.write_xdr(w)?; + } - Ok(()) + Ok(()) + }) } } @@ -1380,36 +1606,40 @@ impl<'a, const MAX: u32> TryFrom<&'a BytesM> for &'a str { impl ReadXdr for BytesM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(BytesM(vec)) + Ok(BytesM(vec)) + }) } } impl WriteXdr for BytesM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1759,36 +1989,40 @@ impl<'a, const MAX: u32> TryFrom<&'a StringM> for &'a str { impl ReadXdr for StringM { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let len: u32 = u32::read_xdr(r)?; - if len > MAX { - return Err(Error::LengthExceedsMax); - } + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let len: u32 = u32::read_xdr(r)?; + if len > MAX { + return Err(Error::LengthExceedsMax); + } - let mut vec = vec![0u8; len as usize]; - r.read_exact(&mut vec)?; + let mut vec = vec![0u8; len as usize]; + r.read_exact(&mut vec)?; - let pad = &mut [0u8; 3][..pad_len(len as usize)]; - r.read_exact(pad)?; - if pad.iter().any(|b| *b != 0) { - return Err(Error::NonZeroPadding); - } + let pad = &mut [0u8; 3][..pad_len(len as usize)]; + r.read_exact(pad)?; + if pad.iter().any(|b| *b != 0) { + return Err(Error::NonZeroPadding); + } - Ok(StringM(vec)) + Ok(StringM(vec)) + }) } } impl WriteXdr for StringM { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; - len.write_xdr(w)?; + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let len: u32 = self.len().try_into().map_err(|_| Error::LengthExceedsMax)?; + len.write_xdr(w)?; - w.write_all(&self.0)?; + w.write_all(&self.0)?; - w.write_all(&[0u8; 3][..pad_len(len as usize)])?; + w.write_all(&[0u8; 3][..pad_len(len as usize)])?; - Ok(()) + Ok(()) + }) } } @@ -1809,7 +2043,7 @@ where T: ReadXdr, { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { + fn read_xdr(r: &mut DepthLimitedRead) -> Result { // Read the frame header value that contains 1 flag-bit and a 33-bit length. // - The 1 flag bit is 0 when there are more frames for the same record. // - The 31-bit length is the length of the bytes within the frame that @@ -1832,26 +2066,34 @@ where mod tests { use std::io::Cursor; - use super::{Error, ReadXdr, VecM, WriteXdr}; + use super::{ + DepthLimitedRead, DepthLimitedWrite, Error, ReadXdr, VecM, WriteXdr, + DEFAULT_XDR_RW_DEPTH_LIMIT, + }; #[test] pub fn vec_u8_read_without_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 4, 2, 2, 2, 2]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2, 2, 2, 2]); } #[test] pub fn vec_u8_read_with_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); - let v = VecM::::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0, 0]); + let v = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v.to_vec(), vec![2]); } #[test] pub fn vec_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1860,8 +2102,9 @@ mod tests { #[test] pub fn vec_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); - let res = VecM::::read_xdr(&mut buf); + let buf = Cursor::new(vec![0, 0, 0, 1, 2, 3, 0, 0]); + let res = + VecM::::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1872,7 +2115,12 @@ mod tests { pub fn vec_u8_write_without_padding() { let mut buf = vec![]; let v: VecM = vec![2, 2, 2, 2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 4, 2, 2, 2, 2]); } @@ -1880,28 +2128,34 @@ mod tests { pub fn vec_u8_write_with_padding() { let mut buf = vec![]; let v: VecM = vec![2].try_into().unwrap(); - v.write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + v.write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![0, 0, 0, 1, 2, 0, 0, 0]); } #[test] pub fn arr_u8_read_without_padding() { - let mut buf = Cursor::new(vec![2, 2, 2, 2]); - let v = <[u8; 4]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 2, 2, 2]); + let v = <[u8; 4]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2, 2, 2, 2]); } #[test] pub fn arr_u8_read_with_padding() { - let mut buf = Cursor::new(vec![2, 0, 0, 0]); - let v = <[u8; 1]>::read_xdr(&mut buf).unwrap(); + let buf = Cursor::new(vec![2, 0, 0, 0]); + let v = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)) + .unwrap(); assert_eq!(v, [2]); } #[test] pub fn arr_u8_read_with_insufficient_padding() { - let mut buf = Cursor::new(vec![2, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::Io(_)) => (), _ => panic!("expected IO error got {res:?}"), @@ -1910,8 +2164,8 @@ mod tests { #[test] pub fn arr_u8_read_with_non_zero_padding() { - let mut buf = Cursor::new(vec![2, 3, 0, 0]); - let res = <[u8; 1]>::read_xdr(&mut buf); + let buf = Cursor::new(vec![2, 3, 0, 0]); + let res = <[u8; 1]>::read_xdr(&mut DepthLimitedRead::new(buf, DEFAULT_XDR_RW_DEPTH_LIMIT)); match res { Err(Error::NonZeroPadding) => (), _ => panic!("expected NonZeroPadding got {res:?}"), @@ -1922,7 +2176,10 @@ mod tests { pub fn arr_u8_write_without_padding() { let mut buf = vec![]; [2u8, 2, 2, 2] - .write_xdr(&mut Cursor::new(&mut buf)) + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) .unwrap(); assert_eq!(buf, vec![2, 2, 2, 2]); } @@ -1930,14 +2187,19 @@ mod tests { #[test] pub fn arr_u8_write_with_padding() { let mut buf = vec![]; - [2u8].write_xdr(&mut Cursor::new(&mut buf)).unwrap(); + [2u8] + .write_xdr(&mut DepthLimitedWrite::new( + Cursor::new(&mut buf), + DEFAULT_XDR_RW_DEPTH_LIMIT, + )) + .unwrap(); assert_eq!(buf, vec![2, 0, 0, 0]); } } #[cfg(all(test, feature = "std"))] mod test { - use super::VecM; + use super::*; #[test] fn into_option_none() { @@ -1962,6 +2224,45 @@ mod test { let v: VecM<_, 1> = vec![1].try_into().unwrap(); assert_eq!(v.to_option(), Some(1)); } + + #[test] + fn depth_limited_read_write_under_the_limit_success() { + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), 4); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), 4); + let a_back: Option>> = ReadXdr::read_xdr(&mut dlr).unwrap(); + assert_eq!(a, a_back); + } + + #[test] + fn write_over_depth_limit_fail() { + let depth_limit = 3; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), depth_limit); + let res = a.write_xdr(&mut buf); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } + + #[test] + fn read_over_depth_limit_fail() { + let read_depth_limit = 3; + let write_depth_limit = 5; + let a: Option>> = Some(Some(Some(5))); + let mut buf = DepthLimitedWrite::new(Vec::new(), write_depth_limit); + a.write_xdr(&mut buf).unwrap(); + + let mut dlr = DepthLimitedRead::new(Cursor::new(buf.inner.as_slice()), read_depth_limit); + let res: Result>>> = ReadXdr::read_xdr(&mut dlr); + match res { + Err(Error::DepthLimitExceeded) => (), + _ => panic!("expected DepthLimitExceeded got {res:?}"), + } + } } #[cfg(all(test, not(feature = "alloc")))] @@ -2074,18 +2375,22 @@ Self::Multi => "Multi", impl ReadXdr for UnionKey { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let e = i32::read_xdr(r)?; - let v: Self = e.try_into()?; - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let e = i32::read_xdr(r)?; + let v: Self = e.try_into()?; + Ok(v) + }) } } impl WriteXdr for UnionKey { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - let i: i32 = (*self).into(); - i.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + let i: i32 = (*self).into(); + i.write_xdr(w) + }) } } @@ -2168,29 +2473,33 @@ Self::Multi(_) => UnionKey::Multi, impl ReadXdr for MyUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let dv: UnionKey = ::read_xdr(r)?; - #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - let v = match dv { - UnionKey::Error => Self::Error(i32::read_xdr(r)?), + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let dv: UnionKey = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + UnionKey::Error => Self::Error(i32::read_xdr(r)?), UnionKey::Multi => Self::Multi(VecM::::read_xdr(r)?), - #[allow(unreachable_patterns)] - _ => return Err(Error::Invalid), - }; - Ok(v) + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) } } impl WriteXdr for MyUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.discriminant().write_xdr(w)?; - #[allow(clippy::match_same_arms)] - match self { - Self::Error(v) => v.write_xdr(w)?, + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::Error(v) => v.write_xdr(w)?, Self::Multi(v) => v.write_xdr(w)?, - }; - Ok(()) + }; + Ok(()) + }) } } @@ -2272,29 +2581,33 @@ Self::V1(_) => 1, impl ReadXdr for IntUnion { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let dv: i32 = ::read_xdr(r)?; - #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - let v = match dv { - 0 => Self::V0(i32::read_xdr(r)?), + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let dv: i32 = ::read_xdr(r)?; + #[allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] + let v = match dv { + 0 => Self::V0(i32::read_xdr(r)?), 1 => Self::V1(VecM::::read_xdr(r)?), - #[allow(unreachable_patterns)] - _ => return Err(Error::Invalid), - }; - Ok(v) + #[allow(unreachable_patterns)] + _ => return Err(Error::Invalid), + }; + Ok(v) + }) } } impl WriteXdr for IntUnion { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.discriminant().write_xdr(w)?; - #[allow(clippy::match_same_arms)] - match self { - Self::V0(v) => v.write_xdr(w)?, + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w| { + self.discriminant().write_xdr(w)?; + #[allow(clippy::match_same_arms)] + match self { + Self::V0(v) => v.write_xdr(w)?, Self::V1(v) => v.write_xdr(w)?, - }; - Ok(()) + }; + Ok(()) + }) } } @@ -2331,17 +2644,19 @@ impl AsRef for IntUnion2 { impl ReadXdr for IntUnion2 { #[cfg(feature = "std")] - fn read_xdr(r: &mut impl Read) -> Result { - let i = IntUnion::read_xdr(r)?; - let v = IntUnion2(i); - Ok(v) + fn read_xdr(r: &mut DepthLimitedRead) -> Result { + r.with_limited_depth(|r| { + let i = IntUnion::read_xdr(r)?; + let v = IntUnion2(i); + Ok(v) + }) } } impl WriteXdr for IntUnion2 { #[cfg(feature = "std")] - fn write_xdr(&self, w: &mut impl Write) -> Result<()> { - self.0.write_xdr(w) + fn write_xdr(&self, w: &mut DepthLimitedWrite) -> Result<()> { + w.with_limited_depth(|w|{ self.0.write_xdr(w) }) } } @@ -2455,26 +2770,26 @@ TypeVariant::IntUnion2, ]; #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { match v { - TypeVariant::SError => Ok(Self::SError(Box::new(SError::read_xdr(r)?))), -TypeVariant::Multi => Ok(Self::Multi(Box::new(Multi::read_xdr(r)?))), -TypeVariant::UnionKey => Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?))), -TypeVariant::MyUnion => Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?))), -TypeVariant::IntUnion => Ok(Self::IntUnion(Box::new(IntUnion::read_xdr(r)?))), -TypeVariant::IntUnion2 => Ok(Self::IntUnion2(Box::new(IntUnion2::read_xdr(r)?))), + TypeVariant::SError => r.with_limited_depth(|r| Ok(Self::SError(Box::new(SError::read_xdr(r)?)))), +TypeVariant::Multi => r.with_limited_depth(|r| Ok(Self::Multi(Box::new(Multi::read_xdr(r)?)))), +TypeVariant::UnionKey => r.with_limited_depth(|r| Ok(Self::UnionKey(Box::new(UnionKey::read_xdr(r)?)))), +TypeVariant::MyUnion => r.with_limited_depth(|r| Ok(Self::MyUnion(Box::new(MyUnion::read_xdr(r)?)))), +TypeVariant::IntUnion => r.with_limited_depth(|r| Ok(Self::IntUnion(Box::new(IntUnion::read_xdr(r)?)))), +TypeVariant::IntUnion2 => r.with_limited_depth(|r| Ok(Self::IntUnion2(Box::new(IntUnion2::read_xdr(r)?)))), } } #[cfg(feature = "base64")] - pub fn read_xdr_base64(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] - pub fn read_xdr_to_end(v: TypeVariant, r: &mut impl Read) -> Result { + pub fn read_xdr_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { let s = Self::read_xdr(v, r)?; // Check that any further reads, such as this read of one byte, read no // data, indicating EOF. If a byte is read the data is invalid. @@ -2486,55 +2801,55 @@ TypeVariant::IntUnion2 => Ok(Self::IntUnion2(Box::new(IntUnion2::read_xdr(r)?))) } #[cfg(feature = "base64")] - pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut impl Read) -> Result { - let mut dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_to_end(v: TypeVariant, r: &mut DepthLimitedRead) -> Result { + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD), r.depth_remaining); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(r).map(|r| r.map(|t| Self::SError(Box::new(t))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(r).map(|r| r.map(|t| Self::Multi(Box::new(t))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(r).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(r).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(r).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(r).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), } } #[cfg(feature = "std")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { + pub fn read_xdr_framed_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::SError(Box::new(t.0))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::Multi(Box::new(t.0))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::IntUnion(Box::new(t.0))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, Frame>::new(r).map(|r| r.map(|t| Self::IntUnion2(Box::new(t.0))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t.0))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t.0))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t.0))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t.0))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t.0))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, Frame>::new(&mut r.inner, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t.0))))), } } #[cfg(feature = "base64")] #[allow(clippy::too_many_lines)] - pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut R) -> Box> + '_> { - let dec = base64::read::DecoderReader::new(r, base64::STANDARD); + pub fn read_xdr_base64_iter(v: TypeVariant, r: &mut DepthLimitedRead) -> Box> + '_> { + let dec = base64::read::DecoderReader::new(&mut r.inner, base64::STANDARD); match v { - TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(dec).map(|r| r.map(|t| Self::SError(Box::new(t))))), -TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(dec).map(|r| r.map(|t| Self::Multi(Box::new(t))))), -TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), -TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), -TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(dec).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), -TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), + TypeVariant::SError => Box::new(ReadXdrIter::<_, SError>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::SError(Box::new(t))))), +TypeVariant::Multi => Box::new(ReadXdrIter::<_, Multi>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::Multi(Box::new(t))))), +TypeVariant::UnionKey => Box::new(ReadXdrIter::<_, UnionKey>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::UnionKey(Box::new(t))))), +TypeVariant::MyUnion => Box::new(ReadXdrIter::<_, MyUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::MyUnion(Box::new(t))))), +TypeVariant::IntUnion => Box::new(ReadXdrIter::<_, IntUnion>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion(Box::new(t))))), +TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec, r.depth_remaining).map(|r| r.map(|t| Self::IntUnion2(Box::new(t))))), } } #[cfg(feature = "std")] pub fn from_xdr>(v: TypeVariant, bytes: B) -> Result { - let mut cursor = Cursor::new(bytes.as_ref()); + let mut cursor = DepthLimitedRead::new(Cursor::new(bytes.as_ref()), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut cursor)?; Ok(t) } @@ -2542,7 +2857,7 @@ TypeVariant::IntUnion2 => Box::new(ReadXdrIter::<_, IntUnion2>::new(dec).map(|r| #[cfg(feature = "base64")] pub fn from_xdr_base64(v: TypeVariant, b64: String) -> Result { let mut b64_reader = Cursor::new(b64); - let mut dec = base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD); + let mut dec = DepthLimitedRead::new(base64::read::DecoderReader::new(&mut b64_reader, base64::STANDARD), DEFAULT_XDR_RW_DEPTH_LIMIT); let t = Self::read_xdr_to_end(v, &mut dec)?; Ok(t) }