diff --git a/avbroot/src/format/avb.rs b/avbroot/src/format/avb.rs index d8e64fb..e2305d6 100644 --- a/avbroot/src/format/avb.rs +++ b/avbroot/src/format/avb.rs @@ -44,6 +44,19 @@ pub const FOOTER_VERSION_MINOR: u32 = 0; pub const HEADER_MAGIC: [u8; 4] = *b"AVB0"; pub const FOOTER_MAGIC: [u8; 4] = *b"AVBf"; +/// Maximum header size. This is the same limit as what avbtool enforces. This +/// value is also used as the limit for individual descriptor fields to allow +/// for early fail. No individual field can actually be this size. +pub const HEADER_MAX_SIZE: u64 = 64 * 1024; + +/// Maximum hash tree size. The current limit equals the hash tree size for a +/// 4GiB image using SHA512 digests and a block size of 4096. +pub const HASH_TREE_MAX_SIZE: u64 = 68_177_920; + +/// Maximum FEC data size. The current limit equals the FEC data size for a 4GiB +/// image using 2 parity bytes per codeword. +pub const FEC_DATA_MAX_SIZE: u64 = 33_959_936; + #[derive(Debug, Error)] pub enum Error { #[error("Failed to read {0:?} field: {1}")] @@ -54,14 +67,14 @@ pub enum Error { StringNotNullTerminated(&'static str), #[error("{0:?} field is not ASCII encoded: {1:?}")] StringNotAscii(&'static str, String), - #[error("{0:?} field exceeds integer bounds")] - IntegerTooLarge(&'static str), + #[error("Header exceeds maximum size of {HEADER_MAX_SIZE}")] + HeaderTooLarge, #[error("Descriptor padding is too long or data was not consumed")] PaddingTooLong, #[error("{0:?} field padding contains non-zero bytes")] PaddingNotZero(&'static str), - #[error("{0:?} field size does not equal size of contained items")] - IncorrectCombinedSize(&'static str), + #[error("{0:?} field is out of bounds")] + FieldOutOfBounds(&'static str), #[error("Invalid VBMeta header magic: {0:?}")] InvalidHeaderMagic([u8; 4]), #[error("Invalid VBMeta footer magic: {0:?}")] @@ -263,30 +276,34 @@ impl FromReader for PropertyDescriptor { type Error = Error; fn from_reader(mut reader: R) -> Result { - let key_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("key_size"))?; - let value_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("value_size"))?; + let key_size = reader.read_u64::()?; + let value_size = reader.read_u64::()?; + + if key_size > HEADER_MAX_SIZE { + return Err(Error::FieldOutOfBounds("key_size")); + } else if value_size > HEADER_MAX_SIZE { + return Err(Error::FieldOutOfBounds("value_size")); + } let key = reader - .read_string_exact(key_size) + .read_string_exact(key_size as usize) .map_err(|e| Error::ReadFieldError("key", e))?; let mut null = [0u8; 1]; - reader.read_exact(&mut null)?; + reader + .read_exact(&mut null) + .map_err(|e| Error::ReadFieldError("key_null", e))?; if null[0] != b'\0' { return Err(Error::StringNotNullTerminated("key")); } - let mut value = vec![0u8; value_size]; + let mut value = vec![0u8; value_size as usize]; reader.read_exact(&mut value)?; // The non-string value is also null terminated. - reader.read_exact(&mut null)?; + reader + .read_exact(&mut null) + .map_err(|e| Error::ReadFieldError("value_null", e))?; if null[0] != b'\0' { return Err(Error::StringNotNullTerminated("value")); } @@ -299,8 +316,14 @@ impl ToWriter for PropertyDescriptor { type Error = Error; fn to_writer(&self, mut writer: W) -> Result<()> { - writer.write_u64::(self.key.len().to_u64().unwrap())?; - writer.write_u64::(self.value.len().to_u64().unwrap())?; + if self.key.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("key_size")); + } else if self.value.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("value_size")); + } + + writer.write_u64::(self.key.len() as u64)?; + writer.write_u64::(self.value.len() as u64)?; writer.write_all(self.key.as_bytes())?; writer.write_all(b"\0")?; writer.write_all(&self.value)?; @@ -531,16 +554,15 @@ impl HashTreeDescriptor { fn get_fec(&self) -> Result<(Fec, usize)> { if self.fec_num_roots == 0 { return Err(Error::FecMissing); + } else if self.fec_size > FEC_DATA_MAX_SIZE { + return Err(Error::FieldOutOfBounds("fec_size")); } - let fec_size = self - .fec_size - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("fec_size"))?; + // Fec will check the validity of this field. let parity = self .fec_num_roots .to_u8() - .ok_or_else(|| Error::IntegerTooLarge("fec_num_roots"))?; + .ok_or_else(|| Error::FieldOutOfBounds("fec_num_roots"))?; // The FEC covers the hash tree as well. let fec = Fec::new( @@ -549,7 +571,7 @@ impl HashTreeDescriptor { parity, )?; - Ok((fec, fec_size)) + Ok((fec, self.fec_size as usize)) } /// Update the root hash, hash tree, and FEC data. The hash tree and FEC @@ -582,14 +604,17 @@ impl HashTreeDescriptor { cancel_signal, )?; - let tree_size = hash_tree - .len() - .to_u64() - .ok_or_else(|| Error::IntegerTooLarge("tree_size"))?; + if hash_tree.len() > HASH_TREE_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("tree_size")); + } + + let tree_size = hash_tree.len() as u64; let mut writer = open_output()?; writer.seek(SeekFrom::Start(self.image_size))?; - writer.write_all(&hash_tree)?; + writer + .write_all(&hash_tree) + .map_err(|e| Error::WriteFieldError("hash_tree", e))?; // The FEC data section is optional. if self.fec_num_roots != 0 { @@ -603,7 +628,7 @@ impl HashTreeDescriptor { let parity = self .fec_num_roots .to_u8() - .ok_or_else(|| Error::IntegerTooLarge("fec_num_roots"))?; + .ok_or_else(|| Error::FieldOutOfBounds("fec_num_roots"))?; // The FEC covers the hash tree as well. let fec = Fec::new(self.image_size + tree_size, self.data_block_size, parity)?; @@ -612,10 +637,12 @@ impl HashTreeDescriptor { let fec_size = fec_data .len() .to_u64() - .ok_or_else(|| Error::IntegerTooLarge("fec_size"))?; + .ok_or_else(|| Error::FieldOutOfBounds("fec_size"))?; // Already seeked to FEC. - writer.write_all(&fec_data)?; + writer + .write_all(&fec_data) + .map_err(|e| Error::WriteFieldError("fec_data", e))?; self.fec_offset = self.image_size + tree_size; self.fec_size = fec_size; @@ -639,10 +666,10 @@ impl HashTreeDescriptor { self.check_offsets()?; let algorithm = ring_algorithm(&self.hash_algorithm)?; - let tree_size = self - .tree_size - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("tree_size"))?; + + if self.tree_size > HASH_TREE_MAX_SIZE { + return Err(Error::FieldOutOfBounds("tree_size")); + } let (actual_root_digest, actual_hash_tree) = Self::calculate_hash_tree( &open_input, @@ -663,8 +690,10 @@ impl HashTreeDescriptor { let mut reader = open_input()?; reader.seek(SeekFrom::Start(self.tree_offset))?; - let mut hash_tree = vec![0u8; tree_size]; - reader.read_exact(&mut hash_tree)?; + let mut hash_tree = vec![0u8; self.tree_size as usize]; + reader + .read_exact(&mut hash_tree) + .map_err(|e| Error::ReadFieldError("hash_tree", e))?; if hash_tree != actual_hash_tree { // These are multiple megabytes, so only report the hashes. @@ -683,7 +712,9 @@ impl HashTreeDescriptor { let mut fec_data = vec![0u8; fec_size]; // Already seeked to FEC. - reader.read_exact(&mut fec_data)?; + reader + .read_exact(&mut fec_data) + .map_err(|e| Error::ReadFieldError("fec_data", e))?; fec.verify(open_input, &fec_data, cancel_signal)?; } @@ -719,7 +750,9 @@ impl HashTreeDescriptor { let mut fec_data = vec![0u8; fec_size]; // Already seeked to FEC. - reader.read_exact(&mut fec_data)?; + reader + .read_exact(&mut fec_data) + .map_err(|e| Error::ReadFieldError("fec_data", e))?; fec.repair(open_input, open_output, &fec_data, cancel_signal)?; @@ -757,18 +790,26 @@ impl FromReader for HashTreeDescriptor { let root_digest_len = reader.read_u32::()?; let flags = reader.read_u32::()?; + if partition_name_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if salt_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("salt_len")); + } else if root_digest_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("root_digest_len")); + } + let mut reserved = [0u8; 60]; reader.read_exact(&mut reserved)?; // Not NULL-terminated. let partition_name = reader - .read_string_exact(partition_name_len.to_usize().unwrap()) + .read_string_exact(partition_name_len as usize) .map_err(|e| Error::ReadFieldError("partition_name", e))?; - let mut salt = vec![0u8; salt_len.to_usize().unwrap()]; + let mut salt = vec![0u8; salt_len as usize]; reader.read_exact(&mut salt)?; - let mut root_digest = vec![0u8; root_digest_len.to_usize().unwrap()]; + let mut root_digest = vec![0u8; root_digest_len as usize]; reader.read_exact(&mut root_digest)?; let descriptor = Self { @@ -797,6 +838,14 @@ impl ToWriter for HashTreeDescriptor { type Error = Error; fn to_writer(&self, mut writer: W) -> Result<()> { + if self.partition_name.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if self.salt.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("salt_len")); + } else if self.root_digest.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("root_digest_len")); + } + writer.write_u32::(self.dm_verity_version)?; writer.write_u64::(self.image_size)?; writer.write_u64::(self.tree_offset)?; @@ -817,27 +866,9 @@ impl ToWriter for HashTreeDescriptor { .write_string_padded(&self.hash_algorithm, 32) .map_err(|e| Error::WriteFieldError("hash_algorithm", e))?; - let partition_name_len = self - .partition_name - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("partition_name_len"))?; - writer.write_u32::(partition_name_len)?; - - let salt_len = self - .salt - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("salt_len"))?; - writer.write_u32::(salt_len)?; - - let root_digest_len = self - .root_digest - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("root_digest_len"))?; - writer.write_u32::(root_digest_len)?; - + writer.write_u32::(self.partition_name.len() as u32)?; + writer.write_u32::(self.salt.len() as u32)?; + writer.write_u32::(self.root_digest.len() as u32)?; writer.write_u32::(self.flags)?; writer.write_all(&self.reserved)?; writer.write_all(self.partition_name.as_bytes())?; @@ -941,18 +972,26 @@ impl FromReader for HashDescriptor { let root_digest_len = reader.read_u32::()?; let flags = reader.read_u32::()?; + if partition_name_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if salt_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("salt_len")); + } else if root_digest_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("root_digest_len")); + } + let mut reserved = [0u8; 60]; reader.read_exact(&mut reserved)?; // Not NULL-terminated. let partition_name = reader - .read_string_exact(partition_name_len.to_usize().unwrap()) + .read_string_exact(partition_name_len as usize) .map_err(|e| Error::ReadFieldError("partition_name", e))?; - let mut salt = vec![0u8; salt_len.to_usize().unwrap()]; + let mut salt = vec![0u8; salt_len as usize]; reader.read_exact(&mut salt)?; - let mut root_digest = vec![0u8; root_digest_len.to_usize().unwrap()]; + let mut root_digest = vec![0u8; root_digest_len as usize]; reader.read_exact(&mut root_digest)?; let descriptor = Self { @@ -973,6 +1012,14 @@ impl ToWriter for HashDescriptor { type Error = Error; fn to_writer(&self, mut writer: W) -> Result<()> { + if self.partition_name.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if self.salt.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("salt_len")); + } else if self.root_digest.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("root_digest_len")); + } + writer.write_u64::(self.image_size)?; if !self.hash_algorithm.is_ascii() { @@ -985,27 +1032,9 @@ impl ToWriter for HashDescriptor { .write_string_padded(&self.hash_algorithm, 32) .map_err(|e| Error::WriteFieldError("hash_algorithm", e))?; - let partition_name_len = self - .partition_name - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("partition_name_len"))?; - writer.write_u32::(partition_name_len)?; - - let salt_len = self - .salt - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("salt_len"))?; - writer.write_u32::(salt_len)?; - - let root_digest_len = self - .root_digest - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("root_digest_len"))?; - writer.write_u32::(root_digest_len)?; - + writer.write_u32::(self.partition_name.len() as u32)?; + writer.write_u32::(self.salt.len() as u32)?; + writer.write_u32::(self.root_digest.len() as u32)?; writer.write_u32::(self.flags)?; writer.write_all(&self.reserved)?; writer.write_all(self.partition_name.as_bytes())?; @@ -1033,9 +1062,13 @@ impl FromReader for KernelCmdlineDescriptor { let flags = reader.read_u32::()?; let cmdline_len = reader.read_u32::()?; + if cmdline_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("cmdline_len")); + } + // Not NULL-terminated. let cmdline = reader - .read_string_exact(cmdline_len.to_usize().unwrap()) + .read_string_exact(cmdline_len as usize) .map_err(|e| Error::ReadFieldError("cmdline", e))?; let descriptor = Self { flags, cmdline }; @@ -1048,15 +1081,12 @@ impl ToWriter for KernelCmdlineDescriptor { type Error = Error; fn to_writer(&self, mut writer: W) -> Result<()> { + if self.cmdline.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("cmdline_len")); + } + writer.write_u32::(self.flags)?; - - let cmdline_len = self - .cmdline - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("cmdline_len"))?; - writer.write_u32::(cmdline_len)?; - + writer.write_u32::(self.cmdline.len() as u32)?; writer.write_all(self.cmdline.as_bytes())?; Ok(()) @@ -1096,15 +1126,21 @@ impl FromReader for ChainPartitionDescriptor { let partition_name_len = reader.read_u32::()?; let public_key_len = reader.read_u32::()?; + if partition_name_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if public_key_len > HEADER_MAX_SIZE as u32 { + return Err(Error::FieldOutOfBounds("public_key_len")); + } + let mut reserved = [0u8; 64]; reader.read_exact(&mut reserved)?; // Not NULL-terminated. let partition_name = reader - .read_string_padded(partition_name_len.to_usize().unwrap()) + .read_string_padded(partition_name_len as usize) .map_err(|e| Error::ReadFieldError("partition_name", e))?; - let mut public_key = vec![0u8; public_key_len.to_usize().unwrap()]; + let mut public_key = vec![0u8; public_key_len as usize]; reader.read_exact(&mut public_key)?; let descriptor = Self { @@ -1122,22 +1158,15 @@ impl ToWriter for ChainPartitionDescriptor { type Error = Error; fn to_writer(&self, mut writer: W) -> Result<()> { + if self.partition_name.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("partition_name_len")); + } else if self.public_key.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("public_key_len")); + } + writer.write_u32::(self.rollback_index_location)?; - - let partition_name_len = self - .partition_name - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("partition_name_len"))?; - writer.write_u32::(partition_name_len)?; - - let public_key_len = self - .public_key - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("public_key_len"))?; - writer.write_u32::(public_key_len)?; - + writer.write_u32::(self.partition_name.len() as u32)?; + writer.write_u32::(self.public_key.len() as u32)?; writer.write_all(&self.reserved)?; writer.write_all(self.partition_name.as_bytes())?; writer.write_all(&self.public_key)?; @@ -1177,9 +1206,13 @@ impl FromReader for Descriptor { fn from_reader(mut reader: R) -> Result { let tag = reader.read_u64::()?; - let nbf_len = reader.read_u64::()?; + let nbf = reader.read_u64::()?; - let mut inner_reader = CountingReader::new(reader.take(nbf_len)); + if nbf > HEADER_MAX_SIZE { + return Err(Error::FieldOutOfBounds("num_bytes_following")); + } + + let mut inner_reader = CountingReader::new(reader.take(nbf)); let descriptor = match tag { PropertyDescriptor::TAG => { @@ -1203,10 +1236,7 @@ impl FromReader for Descriptor { Self::ChainPartition(d) } _ => { - let nbf = nbf_len - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("num_bytes_following"))?; - let mut data = vec![0u8; nbf]; + let mut data = vec![0u8; nbf as usize]; inner_reader.read_exact(&mut data)?; Self::Unknown { tag, data } @@ -1215,7 +1245,7 @@ impl FromReader for Descriptor { // The descriptor data is always aligned to 8 bytes. padding::read_discard(&mut inner_reader, 8)?; - if inner_reader.stream_position()? != nbf_len { + if inner_reader.stream_position()? != nbf { return Err(Error::PaddingTooLong); } @@ -1257,16 +1287,18 @@ impl ToWriter for Descriptor { }; let inner_data = inner_writer.into_inner(); - let inner_len = inner_data.len().to_u64().unwrap(); - let padding_len = padding::calc(inner_len, 8); - let nbf = inner_len - .checked_add(padding_len) - .ok_or_else(|| Error::IntegerTooLarge("num_bytes_following"))?; + + if inner_data.len() > HEADER_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("num_bytes_following")); + } + + let padding_len = padding::calc(inner_data.len(), 8); + let nbf = inner_data.len() + padding_len; writer.write_u64::(tag)?; - writer.write_u64::(nbf)?; + writer.write_u64::(nbf as u64)?; writer.write_all(&inner_data)?; - writer.write_zeros_exact(padding_len)?; + writer.write_zeros_exact(padding_len as u64)?; Ok(()) } @@ -1371,61 +1403,61 @@ impl Header { // Auth block. - let hash_offset = 0u64; - let hash_size = self.hash.len().to_u64().unwrap(); - - let signature_offset = hash_offset - .checked_add(hash_size) - .ok_or_else(|| Error::IntegerTooLarge("signature_offset"))?; - let signature_size = self.signature.len().to_u64().unwrap(); - - let auth_block_data_size = signature_offset - .checked_add(signature_size) - .ok_or_else(|| Error::IntegerTooLarge("authentication_data_block_size"))?; + let auth_block_data_size = self + .hash + .len() + .checked_add(self.signature.len()) + .ok_or_else(|| Error::FieldOutOfBounds("auth_block_data_size"))?; let auth_block_padding_size = padding::calc(auth_block_data_size, 64); let auth_block_size = auth_block_data_size .checked_add(auth_block_padding_size) - .ok_or_else(|| Error::IntegerTooLarge("authentication_data_block_size"))?; + .ok_or_else(|| Error::FieldOutOfBounds("auth_block_size"))?; + + let hash_offset = 0usize; + let signature_offset = hash_offset + self.hash.len(); // Aux block. - let descriptors_offset = 0u64; - let descriptors_size = descriptors_raw.len().to_u64().unwrap(); - - let public_key_offset = descriptors_offset - .checked_add(descriptors_size) - .ok_or_else(|| Error::IntegerTooLarge("public_key_offset"))?; - let public_key_size = self.public_key.len().to_u64().unwrap(); - - let public_key_metadata_offset = public_key_offset - .checked_add(public_key_size) - .ok_or_else(|| Error::IntegerTooLarge("public_key_metadata_offset"))?; - let public_key_metadata_size = self.public_key_metadata.len().to_u64().unwrap(); - - let aux_block_data_size = public_key_metadata_offset - .checked_add(public_key_metadata_size) - .ok_or_else(|| Error::IntegerTooLarge("auxiliary_data_block_size"))?; + let aux_block_data_size = descriptors_raw + .len() + .checked_add(self.public_key.len()) + .and_then(|s| s.checked_add(self.public_key_metadata.len())) + .ok_or_else(|| Error::FieldOutOfBounds("aux_block_data_size"))?; let aux_block_padding_size = padding::calc(aux_block_data_size, 64); let aux_block_size = aux_block_data_size .checked_add(aux_block_padding_size) - .ok_or_else(|| Error::IntegerTooLarge("auxiliary_data_block_size"))?; + .ok_or_else(|| Error::FieldOutOfBounds("aux_block_size"))?; + + let descriptors_offset = 0usize; + let public_key_offset = descriptors_offset + descriptors_raw.len(); + let public_key_metadata_offset = public_key_offset + self.public_key.len(); + + let total_size = Self::SIZE + .checked_add(auth_block_data_size) + .and_then(|s| s.checked_add(aux_block_data_size)) + .ok_or_else(|| Error::FieldOutOfBounds("total_size"))?; + if total_size > HEADER_MAX_SIZE as usize { + return Err(Error::HeaderTooLarge); + } + + // All sizes and offsets are now guaranteed to fit in a u64. writer.write_all(&HEADER_MAGIC)?; writer.write_u32::(self.required_libavb_version_major)?; writer.write_u32::(self.required_libavb_version_minor)?; - writer.write_u64::(auth_block_size)?; - writer.write_u64::(aux_block_size)?; + writer.write_u64::(auth_block_size as u64)?; + writer.write_u64::(aux_block_size as u64)?; writer.write_u32::(self.algorithm_type.to_raw())?; - writer.write_u64::(hash_offset)?; - writer.write_u64::(hash_size)?; - writer.write_u64::(signature_offset)?; - writer.write_u64::(signature_size)?; - writer.write_u64::(public_key_offset)?; - writer.write_u64::(public_key_size)?; - writer.write_u64::(public_key_metadata_offset)?; - writer.write_u64::(public_key_metadata_size)?; - writer.write_u64::(descriptors_offset)?; - writer.write_u64::(descriptors_size)?; + writer.write_u64::(hash_offset as u64)?; + writer.write_u64::(self.hash.len() as u64)?; + writer.write_u64::(signature_offset as u64)?; + writer.write_u64::(self.signature.len() as u64)?; + writer.write_u64::(public_key_offset as u64)?; + writer.write_u64::(self.public_key.len() as u64)?; + writer.write_u64::(public_key_metadata_offset as u64)?; + writer.write_u64::(self.public_key_metadata.len() as u64)?; + writer.write_u64::(descriptors_offset as u64)?; + writer.write_u64::(descriptors_raw.len() as u64)?; writer.write_u64::(self.rollback_index)?; writer.write_u32::(self.flags)?; writer.write_u32::(self.rollback_index_location)?; @@ -1440,14 +1472,14 @@ impl Header { if !skip_auth_block { writer.write_all(&self.hash)?; writer.write_all(&self.signature)?; - writer.write_zeros_exact(auth_block_padding_size)?; + writer.write_zeros_exact(auth_block_padding_size as u64)?; } // Aux block. writer.write_all(&descriptors_raw)?; writer.write_all(&self.public_key)?; writer.write_all(&self.public_key_metadata)?; - writer.write_zeros_exact(aux_block_padding_size)?; + writer.write_zeros_exact(aux_block_padding_size as u64)?; Ok(()) } @@ -1595,7 +1627,9 @@ impl Header { impl FromReader for Header { type Error = Error; - fn from_reader(mut reader: R) -> Result { + fn from_reader(reader: R) -> Result { + let mut reader = CountingReader::new(reader); + let mut magic = [0u8; 4]; reader.read_exact(&mut magic)?; @@ -1605,72 +1639,44 @@ impl FromReader for Header { let required_libavb_version_major = reader.read_u32::()?; let required_libavb_version_minor = reader.read_u32::()?; - let authentication_data_block_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("authentication_data_block_size"))?; - let auxiliary_data_block_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("auxiliary_data_block_size"))?; + let auth_block_size = reader.read_u64::()?; + let aux_block_size = reader.read_u64::()?; let algorithm_type_raw = reader.read_u32::()?; let algorithm_type = AlgorithmType::from_raw(algorithm_type_raw); - let hash_offset = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("hash_offset"))?; - let hash_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("hash_size"))?; - let signature_offset = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("signature_offset"))?; - let signature_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("signature_size"))?; + let hash_offset = reader.read_u64::()?; + let hash_size = reader.read_u64::()?; + let signature_offset = reader.read_u64::()?; + let signature_size = reader.read_u64::()?; let auth_block_combined = hash_size + signature_size; let auth_block_padding = padding::calc(auth_block_combined, 64); - if authentication_data_block_size != auth_block_combined + auth_block_padding { - return Err(Error::IncorrectCombinedSize( - "authentication_data_block_size", - )); + if auth_block_size != auth_block_combined + auth_block_padding { + return Err(Error::FieldOutOfBounds("auth_block_size")); + } else if hash_offset > auth_block_combined - hash_size { + return Err(Error::FieldOutOfBounds("hash_offset")); + } else if signature_offset > auth_block_combined - signature_size { + return Err(Error::FieldOutOfBounds("signature_offset")); } - let public_key_offset = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("public_key_offset"))?; - let public_key_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("public_key_size"))?; - let public_key_metadata_offset = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("public_key_metadata_offset"))?; - let public_key_metadata_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("public_key_metadata_size"))?; - let descriptors_offset = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("descriptors_offset"))?; - let descriptors_size = reader - .read_u64::()? - .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("descriptors_size"))?; + let public_key_offset = reader.read_u64::()?; + let public_key_size = reader.read_u64::()?; + let public_key_metadata_offset = reader.read_u64::()?; + let public_key_metadata_size = reader.read_u64::()?; + let descriptors_offset = reader.read_u64::()?; + let descriptors_size = reader.read_u64::()?; let aux_block_combined = public_key_size + public_key_metadata_size + descriptors_size; let aux_block_padding = padding::calc(aux_block_combined, 64); - if auxiliary_data_block_size != aux_block_combined + aux_block_padding { - return Err(Error::IncorrectCombinedSize("auxiliary_data_block_size")); + if aux_block_size != aux_block_combined + aux_block_padding { + return Err(Error::FieldOutOfBounds("aux_block_size")); + } else if public_key_offset > aux_block_combined - public_key_size { + return Err(Error::FieldOutOfBounds("public_key_offset")); + } else if public_key_metadata_offset > aux_block_combined - public_key_metadata_size { + return Err(Error::FieldOutOfBounds("public_key_metadata_size")); + } else if descriptors_offset > aux_block_combined - descriptors_size { + return Err(Error::FieldOutOfBounds("descriptors_offset")); } let rollback_index = reader.read_u64::()?; @@ -1684,70 +1690,49 @@ impl FromReader for Header { let mut reserved = [0u8; 80]; reader.read_exact(&mut reserved)?; - let mut auth_block = vec![0u8; authentication_data_block_size]; + let header_size = reader.stream_position()?; + let total_size = header_size + .checked_add(auth_block_size) + .and_then(|v| v.checked_add(aux_block_size)) + .ok_or_else(|| Error::FieldOutOfBounds("total_size"))?; + if total_size > HEADER_MAX_SIZE { + return Err(Error::HeaderTooLarge); + } + + // All of the size fields above are now guaranteed to fit in usize. + + let mut auth_block = vec![0u8; auth_block_size as usize]; reader.read_exact(&mut auth_block)?; - let mut aux_block = vec![0u8; auxiliary_data_block_size]; + let mut aux_block = vec![0u8; aux_block_size as usize]; reader.read_exact(&mut aux_block)?; // When we verify() the signatures, we're doing so on re-serialized // fields. The padding is the only thing that can escape this, so make // sure they don't contain any data. if !util::is_zero( - &auth_block[auth_block_combined..auth_block_combined + auth_block_padding], + &auth_block[auth_block_combined as usize..][..auth_block_padding as usize], ) { - return Err(Error::PaddingNotZero("authentication_data_block")); + return Err(Error::PaddingNotZero("auth_block")); } - if !util::is_zero(&aux_block[aux_block_combined..aux_block_combined + aux_block_padding]) { - return Err(Error::PaddingNotZero("auxiliary_data_block")); + if !util::is_zero(&aux_block[aux_block_combined as usize..][..aux_block_padding as usize]) { + return Err(Error::PaddingNotZero("aux_block")); } // Auth block data. - - if hash_offset - .checked_add(hash_size) - .map_or(false, |s| s > auth_block.len()) - { - return Err(Error::IntegerTooLarge("hash_offset + hash_size")); - } - let hash = &auth_block[hash_offset..hash_offset + hash_size]; - - if signature_offset - .checked_add(signature_size) - .map_or(false, |s| s > auth_block.len()) - { - return Err(Error::IntegerTooLarge("signature_offset + signature_size")); - } - let signature = &auth_block[signature_offset..signature_offset + signature_size]; + let hash = &auth_block[hash_offset as usize..][..hash_size as usize]; + let signature = &auth_block[signature_offset as usize..][..signature_size as usize]; // Aux block data. - - if public_key_offset - .checked_add(public_key_size) - .map_or(false, |s| s > aux_block.len()) - { - return Err(Error::IntegerTooLarge( - "public_key_offset + public_key_size", - )); - } - let public_key = &aux_block[public_key_offset..public_key_offset + public_key_size]; - - if public_key_metadata_offset - .checked_add(public_key_metadata_size) - .map_or(false, |s| s > aux_block.len()) - { - return Err(Error::IntegerTooLarge( - "public_key_metadata_offset + public_key_metadata_size", - )); - } - let public_key_metadata = &aux_block - [public_key_metadata_offset..public_key_metadata_offset + public_key_metadata_size]; + let public_key = &aux_block[public_key_offset as usize..][..public_key_size as usize]; + let public_key_metadata = + &aux_block[public_key_metadata_offset as usize..][..public_key_metadata_size as usize]; let mut descriptors: Vec = vec![]; let mut descriptor_reader = Cursor::new(&aux_block); - let mut pos = descriptor_reader.seek(SeekFrom::Start(descriptors_offset as u64))?; + let mut pos = descriptor_reader.seek(SeekFrom::Start(descriptors_offset))?; - while pos < (descriptors_offset + descriptors_size) as u64 { + while pos < descriptors_offset + descriptors_size { let descriptor = Descriptor::from_reader(&mut descriptor_reader)?; descriptors.push(descriptor); pos = descriptor_reader.stream_position()?; @@ -1901,7 +1886,7 @@ pub fn decode_public_key(data: &[u8]) -> Result { let key_bits = reader .read_u32::()? .to_usize() - .ok_or_else(|| Error::IntegerTooLarge("key_bits"))?; + .ok_or_else(|| Error::FieldOutOfBounds("key_bits"))?; // Skip n0inv. reader.read_discard_exact(4)?; @@ -1958,7 +1943,7 @@ fn write_image_internal( let padding_size = padding::write_zeros(&mut writer, block_size)?; eof_image_size .checked_add(padding_size) - .ok_or_else(|| Error::IntegerTooLarge("vbmeta_offset"))? + .ok_or_else(|| Error::FieldOutOfBounds("vbmeta_offset"))? } else { eof_image_size };