diff --git a/rust/README.md b/rust/README.md index 7f7fac3509..aed83968f4 100644 --- a/rust/README.md +++ b/rust/README.md @@ -670,7 +670,7 @@ cargo build ```bash # Run all tests -cargo test --features tests +cargo test --workspace # Run specific test cargo test -p tests --test test_complex_struct diff --git a/rust/fory-core/src/meta/type_meta.rs b/rust/fory-core/src/meta/type_meta.rs index d4ab3a956a..1bb5fd1354 100644 --- a/rust/fory-core/src/meta/type_meta.rs +++ b/rust/fory-core/src/meta/type_meta.rs @@ -60,6 +60,7 @@ use std::collections::HashMap; use std::rc::Rc; const SMALL_NUM_FIELDS_THRESHOLD: usize = 0b11111; +const MAX_TYPE_META_FIELDS: usize = i16::MAX as usize; const REGISTER_BY_NAME_FLAG: u8 = 0b100000; const FIELD_NAME_SIZE_THRESHOLD: usize = 0b1111; /// Marker value in encoding bits to indicate field ID mode (instead of field name) @@ -642,6 +643,9 @@ impl TypeMeta { length as usize }; let bytes = reader.read_bytes(length)?; + if encoding_idx as usize >= encodings.len() { + return Err(Error::invalid_data("encoding_index out of bounds")); + } let encoding = encodings[encoding_idx as usize]; decoder.decode(bytes, encoding) } @@ -817,6 +821,13 @@ impl TypeMeta { if num_fields == SMALL_NUM_FIELDS_THRESHOLD { num_fields += reader.read_varuint32()? as usize; } + // limit the number of fields to prevent potential OOM when creating Vec + if num_fields > MAX_TYPE_META_FIELDS { + return Err(Error::invalid_data(format!( + "too many fields in type meta: {}, max: {}", + num_fields, MAX_TYPE_META_FIELDS + ))); + } let mut type_id; let mut user_type_id = NO_USER_TYPE_ID; let namespace; diff --git a/rust/fory-core/src/serializer/collection.rs b/rust/fory-core/src/serializer/collection.rs index 68a6dc6a4d..ae16620c7e 100644 --- a/rust/fory-core/src/serializer/collection.rs +++ b/rust/fory-core/src/serializer/collection.rs @@ -32,6 +32,19 @@ pub const DECL_ELEMENT_TYPE: u8 = 0b100; // Whether collection elements type same. pub const IS_SAME_TYPE: u8 = 0b1000; +fn check_collection_len(context: &ReadContext, len: u32) -> Result<(), Error> { + if std::mem::size_of::() == 0 { + return Ok(()); + } + let len = len as usize; + let remaining = context.reader.slice_after_cursor().len(); + if len > remaining { + let cursor = context.reader.get_cursor(); + return Err(Error::buffer_out_of_bound(cursor, len, cursor + remaining)); + } + Ok(()) +} + pub fn write_collection_type_info( context: &mut WriteContext, collection_type_id: u32, @@ -245,6 +258,7 @@ where (header & IS_SAME_TYPE) != 0, Error::type_error("Type inconsistent, target type is not polymorphic") ); + check_collection_len::(context, len)?; if !has_null { (0..len) .map(|_| T::fory_read_data(context)) @@ -284,6 +298,7 @@ where (header & IS_SAME_TYPE) != 0, Error::type_error("Type inconsistent, target type is not polymorphic") ); + check_collection_len::(context, len)?; let mut vec = Vec::with_capacity(len as usize); if !has_null { for _ in 0..len { @@ -320,14 +335,14 @@ where } else { RefMode::None }; - - let mut vec = Vec::with_capacity(len as usize); if is_same_type { let type_info = if !is_declared { context.read_any_type_info()? } else { T::fory_get_type_info(context.get_type_resolver())? }; + check_collection_len::(context, len)?; + let mut vec = Vec::with_capacity(len as usize); if elem_ref_mode == RefMode::None { for _ in 0..len { vec.push(T::fory_read_with_type_info( @@ -345,12 +360,15 @@ where )?); } } + Ok(vec) } else { + check_collection_len::(context, len)?; + let mut vec = Vec::with_capacity(len as usize); for _ in 0..len { vec.push(T::fory_read(context, elem_ref_mode, true)?); } + Ok(vec) } - Ok(vec) } /// Slow but versatile collection deserialization for dynamic trait object and shared/circular reference. @@ -382,6 +400,7 @@ where } else { T::fory_get_type_info(context.get_type_resolver())? }; + check_collection_len::(context, len)?; // All elements are same type if elem_ref_mode == RefMode::None { // No null elements, no tracking @@ -395,6 +414,7 @@ where .collect::>() } } else { + check_collection_len::(context, len)?; (0..len) .map(|_| T::fory_read(context, elem_ref_mode, true)) .collect::>() diff --git a/rust/fory-core/src/serializer/map.rs b/rust/fory-core/src/serializer/map.rs index 4e90a1c3d9..f8c7997c07 100644 --- a/rust/fory-core/src/serializer/map.rs +++ b/rust/fory-core/src/serializer/map.rs @@ -34,6 +34,16 @@ const TRACKING_VALUE_REF: u8 = 0b1000; pub const VALUE_NULL: u8 = 0b10000; pub const DECL_VALUE_TYPE: u8 = 0b100000; +fn check_map_len(context: &ReadContext, len: u32) -> Result<(), Error> { + let len = len as usize; + let remaining = context.reader.slice_after_cursor().len(); + if len > remaining { + let cursor = context.reader.get_cursor(); + return Err(Error::buffer_out_of_bound(cursor, len, cursor + remaining)); + } + Ok(()) +} + fn write_chunk_size(context: &mut WriteContext, header_offset: usize, size: u8) { context.writer.set_bytes(header_offset + 1, &[size]); } @@ -547,10 +557,10 @@ impl Result { let len = context.reader.read_varuint32()?; - let mut map = HashMap::::with_capacity(len as usize); if len == 0 { - return Ok(map); + return Ok(HashMap::new()); } + check_map_len(context, len)?; if K::fory_is_polymorphic() || K::fory_is_shared_ref() || V::fory_is_polymorphic() @@ -559,6 +569,7 @@ impl = HashMap::with_capacity(len as usize); return read_hashmap_data_dyn_ref(context, map, len); } + let mut map = HashMap::::with_capacity(len as usize); let mut len_counter = 0; loop { if len_counter == len { @@ -698,10 +709,11 @@ impl Result { let len = context.reader.read_varuint32()?; - let mut map = BTreeMap::::new(); if len == 0 { - return Ok(map); + return Ok(BTreeMap::new()); } + check_map_len(context, len)?; + let mut map = BTreeMap::::new(); if K::fory_is_polymorphic() || K::fory_is_shared_ref() || V::fory_is_polymorphic() diff --git a/rust/fory-core/src/serializer/primitive_list.rs b/rust/fory-core/src/serializer/primitive_list.rs index 2fc8029e32..cd381a7dbf 100644 --- a/rust/fory-core/src/serializer/primitive_list.rs +++ b/rust/fory-core/src/serializer/primitive_list.rs @@ -83,6 +83,15 @@ pub fn fory_read_data(context: &mut ReadContext) -> Result if size_bytes % std::mem::size_of::() != 0 { return Err(Error::invalid_data("Invalid data length")); } + let remaining = context.reader.slice_after_cursor().len(); + if size_bytes > remaining { + let cursor = context.reader.get_cursor(); + return Err(Error::buffer_out_of_bound( + cursor, + size_bytes, + cursor + remaining, + )); + } let len = size_bytes / std::mem::size_of::(); let mut vec: Vec = Vec::with_capacity(len); diff --git a/rust/fory-core/src/serializer/skip.rs b/rust/fory-core/src/serializer/skip.rs index 697158a358..84fbf76476 100644 --- a/rust/fory-core/src/serializer/skip.rs +++ b/rust/fory-core/src/serializer/skip.rs @@ -181,6 +181,9 @@ fn skip_collection(context: &mut ReadContext, field_type: &FieldType) -> Result< let is_same_type = (header & IS_SAME_TYPE) != 0; let skip_ref_flag = is_same_type && !has_null; let is_declared = (header & DECL_ELEMENT_TYPE) != 0; + if field_type.generics.is_empty() { + return Err(Error::invalid_data("empty generics")); + } let default_elem_type = field_type.generics.first().unwrap(); let (type_info, elem_field_type); let elem_type = if is_same_type && !is_declared { @@ -212,6 +215,9 @@ fn skip_map(context: &mut ReadContext, field_type: &FieldType) -> Result<(), Err return Ok(()); } let mut len_counter = 0; + if field_type.generics.len() < 2 { + return Err(Error::invalid_data("map must have at least 2 generics")); + } let default_key_type = field_type.generics.first().unwrap(); let default_value_type = field_type.generics.get(1).unwrap(); loop {