diff --git a/packages/vm/src/errors/communication_error.rs b/packages/vm/src/errors/communication_error.rs index 73a793a52..b9c4e60e3 100644 --- a/packages/vm/src/errors/communication_error.rs +++ b/packages/vm/src/errors/communication_error.rs @@ -25,6 +25,9 @@ pub enum CommunicationError { #[from] source: RegionValidationError, }, + /// When the contract supplies invalid section data to the host. See also `decode_sections` [crate::sections::decode_sections]. + #[error("Got an invalid section: {}", msg)] + InvalidSection { msg: String }, /// Whenever UTF-8 bytes cannot be decoded into a unicode string, e.g. in String::from_utf8 or str::from_utf8. #[error("Cannot decode UTF8 bytes into string: {}", msg)] InvalidUtf8 { msg: String }, @@ -56,6 +59,10 @@ impl CommunicationError { CommunicationError::InvalidOrder { value } } + pub(crate) fn invalid_section(msg: impl Into) -> Self { + CommunicationError::InvalidSection { msg: msg.into() } + } + #[allow(dead_code)] pub(crate) fn invalid_utf8(msg: impl ToString) -> Self { CommunicationError::InvalidUtf8 { diff --git a/packages/vm/src/imports.rs b/packages/vm/src/imports.rs index 581e47b8b..dc8ee955f 100644 --- a/packages/vm/src/imports.rs +++ b/packages/vm/src/imports.rs @@ -730,9 +730,9 @@ pub fn do_ed25519_batch_verify< (EDDSA_PUBKEY_LEN + 4) * MAX_COUNT_ED25519_BATCH, )?; - let messages = decode_sections(&messages); - let signatures = decode_sections(&signatures); - let public_keys = decode_sections(&public_keys); + let messages = decode_sections(&messages)?; + let signatures = decode_sections(&signatures)?; + let public_keys = decode_sections(&public_keys)?; let gas_cost = if public_keys.len() == 1 { &data.gas_config.ed25519_batch_verify_one_pubkey_cost diff --git a/packages/vm/src/sections.rs b/packages/vm/src/sections.rs index c0c40166a..b1a95cbe8 100644 --- a/packages/vm/src/sections.rs +++ b/packages/vm/src/sections.rs @@ -1,13 +1,12 @@ use crate::conversion::to_u32; -use crate::errors::VmResult; +use crate::{CommunicationError, VmResult}; /// Decodes sections of data into multiple slices. /// /// Each encoded section is suffixed by a section length, encoded as big endian uint32. /// -/// See also: `encode_section`. -#[allow(dead_code)] -pub fn decode_sections(data: &[u8]) -> Vec<&[u8]> { +/// See also: [`encode_sections`]. +pub fn decode_sections(data: &[u8]) -> Result, CommunicationError> { let mut result: Vec<&[u8]> = vec![]; let mut remaining_len = data.len(); while remaining_len >= 4 { @@ -17,11 +16,20 @@ pub fn decode_sections(data: &[u8]) -> Vec<&[u8]> { data[remaining_len - 2], data[remaining_len - 1], ]) as usize; - result.push(&data[remaining_len - 4 - tail_len..remaining_len - 4]); - remaining_len -= 4 + tail_len; + let tail_len_idx = remaining_len - 4; // index of the first byte of the tail length + let section_start = tail_len_idx + .checked_sub(tail_len) + .ok_or_else(|| CommunicationError::invalid_section("section length overflow"))?; + result.push(&data[section_start..tail_len_idx]); + remaining_len = section_start; + } + if remaining_len > 0 { + return Err(CommunicationError::invalid_section( + "extra data outside of any section", + )); } result.reverse(); - result + Ok(result) } /// Encodes multiple sections of data into one vector. @@ -57,52 +65,61 @@ mod tests { #[test] fn decode_sections_works_for_empty_sections() { - let dec = decode_sections(&[]); + let dec = decode_sections(&[]).unwrap(); assert_eq!(dec.len(), 0); - let dec = decode_sections(b"\0\0\0\0"); + let dec = decode_sections(b"\0\0\0\0").unwrap(); assert_eq!(dec, &[&[0u8; 0]]); - let dec = decode_sections(b"\0\0\0\0\0\0\0\0"); + let dec = decode_sections(b"\0\0\0\0\0\0\0\0").unwrap(); assert_eq!(dec, &[&[0u8; 0]; 2]); - let dec = decode_sections(b"\0\0\0\0\0\0\0\0\0\0\0\0"); + let dec = decode_sections(b"\0\0\0\0\0\0\0\0\0\0\0\0").unwrap(); assert_eq!(dec, &[&[0u8; 0]; 3]); - // ignores "trailing" stuff - let dec = decode_sections(b"\0\0\0\0\0\0\0\0\0\0\0"); - assert_eq!(dec, &[&[0u8; 0]; 2]); } #[test] fn decode_sections_works_for_one_element() { - let dec = decode_sections(b"\xAA\0\0\0\x01"); + let dec = decode_sections(b"\xAA\0\0\0\x01").unwrap(); assert_eq!(dec, &[vec![0xAA]]); - let dec = decode_sections(b"\xAA\xBB\0\0\0\x02"); + let dec = decode_sections(b"\xAA\xBB\0\0\0\x02").unwrap(); assert_eq!(dec, &[vec![0xAA, 0xBB]]); - let dec = decode_sections(b"\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\0\0\x01\x15"); + let dec = decode_sections(b"\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\0\0\x01\x15").unwrap(); assert_eq!(dec, &[vec![0x9D; 277]]); } #[test] fn decode_sections_works_for_two_elements() { let data = b"\xAA\0\0\0\x01\xBB\xCC\0\0\0\x02".to_vec(); - assert_eq!(decode_sections(&data), &[vec![0xAA], vec![0xBB, 0xCC]]); + assert_eq!( + decode_sections(&data).unwrap(), + &[vec![0xAA], vec![0xBB, 0xCC]] + ); let data = b"\xDE\xEF\x62\0\0\0\x03\0\0\0\0".to_vec(); - assert_eq!(decode_sections(&data), &[vec![0xDE, 0xEF, 0x62], vec![]]); + assert_eq!( + decode_sections(&data).unwrap(), + &[vec![0xDE, 0xEF, 0x62], vec![]] + ); let data = b"\0\0\0\0\xDE\xEF\x62\0\0\0\x03".to_vec(); - assert_eq!(decode_sections(&data), &[vec![], vec![0xDE, 0xEF, 0x62]]); + assert_eq!( + decode_sections(&data).unwrap(), + &[vec![], vec![0xDE, 0xEF, 0x62]] + ); let data = b"\0\0\0\0\0\0\0\0".to_vec(); - assert_eq!(decode_sections(&data), &[vec![0u8; 0], vec![]]); + assert_eq!(decode_sections(&data).unwrap(), &[vec![0u8; 0], vec![]]); let data = b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\x13\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\x9D\0\0\x01\x15".to_vec(); - assert_eq!(decode_sections(&data), &[vec![0xFF; 19], vec![0x9D; 277]]); + assert_eq!( + decode_sections(&data).unwrap(), + &[vec![0xFF; 19], vec![0x9D; 277]] + ); } #[test] fn decode_sections_works_for_multiple_elements() { - let dec = decode_sections(b"\xAA\0\0\0\x01"); + let dec = decode_sections(b"\xAA\0\0\0\x01").unwrap(); assert_eq!(dec, &[vec![0xAA]]); - let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02"); + let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02").unwrap(); assert_eq!(dec, &[vec![0xAA], vec![0xDE, 0xDE]]); - let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0"); + let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0").unwrap(); assert_eq!(dec, &[vec![0xAA], vec![0xDE, 0xDE], vec![]]); - let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\x13"); + let dec = decode_sections(b"\xAA\0\0\0\x01\xDE\xDE\0\0\0\x02\0\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0\x13").unwrap(); assert_eq!(dec, &[vec![0xAA], vec![0xDE, 0xDE], vec![], vec![0xFF; 19]]); }