From 50913db3752d2290618a80610bdc91a462fd9506 Mon Sep 17 00:00:00 2001 From: Andrew Gunnerson Date: Tue, 26 Sep 2023 20:16:46 -0400 Subject: [PATCH] avb: Enforce size limits to prevent DoS AVB 2.0 mandates that the header is 64 KiB or less, so enforce this both when parsing and when writing vbmeta headers. Hash trees and FEC data are also read into memory, so enforce limits corresponding to a 4 GiB partition image. Since these limits are so small, this lets us remove nearly all checked integer conversions since a direct cast will never result in truncation. Issue: #157 Signed-off-by: Andrew Gunnerson --- avbroot/src/format/avb.rs | 531 ++++++++++++++++++-------------------- 1 file changed, 258 insertions(+), 273 deletions(-) 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 };