diff --git a/Cargo.toml b/Cargo.toml index d86f058..b9b2aee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ebml-iterable" -version = "0.6.0" +version = "0.6.1" authors = ["Austin Blake "] edition = "2018" description = "This crate provides an iterator over EBML encoded data. The items provided by the iterator are Tags as defined in EBML. The iterator is spec-agnostic and requires a specification implementing specific traits to read files. Typically, you would only use this crate to implement a custom specification - most often you would prefer a crate providing an existing specification, like `webm-iterable`." diff --git a/README.md b/README.md index b6da46f..52faf5a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ binary version of XML. It's used for container formats like [WebM][webm] or ```Cargo.toml [dependencies] -ebml-iterable = "0.6.0" +ebml-iterable = "0.6.1" ``` # Usage diff --git a/src/errors.rs b/src/errors.rs index a04a4e2..2bc8145 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -120,6 +120,27 @@ pub mod tag_iterator { /// size: usize }, + + /// + /// An error indicating the reader found a tag with an invalid size. + /// + InvalidTagSize { + + /// + /// The position of the element. + /// + position: usize, + + /// + /// The id of the tag that was found. + /// + tag_id: u64, + + /// + /// The size of the tag that was found. + /// + size: usize + }, } impl fmt::Display for CorruptedFileError { @@ -141,7 +162,12 @@ pub mod tag_iterator { position, tag_id, size : _ - } => write!(f, "Found an oversized tag [0x{tag_id:x?}] at position {position}") + } => write!(f, "Found an oversized tag [0x{tag_id:x?}] at position {position}"), + CorruptedFileError::InvalidTagSize { + position, + tag_id, + size, + } => write!(f, "Found an oversized tag [0x{tag_id:x?}] at position {position} with size {size}. Max supported size is 8GB."), } } } diff --git a/src/tag_iterator.rs b/src/tag_iterator.rs index 7f65733..a6c4bd1 100644 --- a/src/tag_iterator.rs +++ b/src/tag_iterator.rs @@ -56,6 +56,7 @@ pub struct TagIterator source: R, tag_ids_to_buffer: HashSet, allowed_errors: u8, + max_allowed_tag_size: Option, buffer: Box<[u8]>, buffer_offset: Option, @@ -93,6 +94,7 @@ impl TagIterator source, tag_ids_to_buffer: tags_to_buffer.iter().map(|tag| tag.get_id()).collect(), allowed_errors: 0, + max_allowed_tag_size: Some(4 * usize::pow(1000, 3)), // 4GB buffer: buffer.into_boxed_slice(), buffered_byte_length: 0, buffer_offset: None, @@ -125,6 +127,15 @@ impl TagIterator }); } + /// + /// Configures the maximum size a tag is allowed to be before the iterator considers it invalid. + /// + /// By default (as of v0.6.1), the iterator will throw an [`CorruptedFileError::InvalidTagSize`] error if it comes across any tags that declare their data to be more than 4GB. This method can be used to change (and optionally remove) this behavior. Note that increasing this size can potentially result in massive allocations, causing delays and panics. + /// + pub fn set_max_allowable_tag_size(&mut self, size: Option) { + self.max_allowed_tag_size = size; + } + /// /// Instructs the iterator to attempt to recover after reaching corrupted file data. /// @@ -307,6 +318,12 @@ impl TagIterator return Err(TagIteratorError::CorruptedFileData(CorruptedFileError::OversizedChildElement{ position: self.current_offset(), tag_id, size: size.value()})); } + if let Some(max_size) = self.max_allowed_tag_size { + if size.is_known() && size.value() > max_size { + return Err(TagIteratorError::CorruptedFileData(CorruptedFileError::InvalidTagSize { position: self.current_offset(), tag_id, size: size.value() })); + } + } + Ok((tag_id, spec_tag_type, size, header_len)) } diff --git a/tests/corrupt_data_tests.rs b/tests/corrupt_data_tests.rs index c1bbc75..b04bdad 100644 --- a/tests/corrupt_data_tests.rs +++ b/tests/corrupt_data_tests.rs @@ -4,7 +4,7 @@ pub mod corrupt_data_tests { use ebml_iterable::error::{TagIteratorError, CorruptedFileError}; use ebml_iterable::iterator::AllowableErrors; use ebml_iterable::specs::Master; - use ebml_iterable::{TagIterator, TagWriter}; + use ebml_iterable::{TagIterator, TagWriter, WriteOptions}; use std::io::Cursor; use super::test_spec::TestSpec; @@ -121,6 +121,48 @@ pub mod corrupt_data_tests { reader.for_each(|t| assert!(t.is_ok())); } + fn get_data_with_6_byte_tag() -> Cursor> { + let tags: Vec = vec![ + TestSpec::Segment(Master::Start), + TestSpec::Cluster(Master::Start), + TestSpec::Block(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06]), + TestSpec::Cluster(Master::End), + TestSpec::Segment(Master::End), + ]; + + let mut dest = Cursor::new(Vec::new()); + let mut writer = TagWriter::new(&mut dest); + + for tag in tags.iter() { + if matches!(tag, TestSpec::Segment(_)) || matches!(tag, TestSpec::Cluster(_)) { + writer.write_advanced(tag, WriteOptions::is_unknown_sized_element()).expect("Test shouldn't error"); + } else { + writer.write(tag).expect("Test shouldn't error"); + } + } + + // // Rewrite size of block element + // dest.get_mut()[25] = 0x09; + // dest.get_mut()[26] = 0x65; + // dest.get_mut()[27] = 0xa0; + // dest.get_mut()[28] = 0xbc; + // dest.get_mut()[29] = 0x00; + + println!("dest {:x?}", dest); + dest.set_position(0); + dest + } + + #[test] + pub fn error_on_oversized_tag() { + let mut cursor = get_data_with_6_byte_tag(); + let mut reader: TagIterator<_, TestSpec> = TagIterator::new(&mut cursor, &[]); + reader.set_max_allowable_tag_size(Some(5)); + assert!(reader.next().unwrap().is_ok()); + assert!(reader.next().unwrap().is_ok()); + assert!(matches!(reader.next().unwrap(), Err(TagIteratorError::CorruptedFileData(CorruptedFileError::InvalidTagSize{position: _, tag_id: _, size: _})))); + } + #[test] pub fn recover_on_global_element() { let tags: Vec = vec![