diff --git a/avbroot/src/format/bootimage.rs b/avbroot/src/format/bootimage.rs index 1a13dd2..a37fd4f 100644 --- a/avbroot/src/format/bootimage.rs +++ b/avbroot/src/format/bootimage.rs @@ -57,6 +57,15 @@ const VENDOR_HDR_V3_SIZE: u32 = 2112; const VENDOR_HDR_V4_EXTRA_SIZE: u32 = 16; const VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE: u32 = 108; +/// Maximum size of any individual boot image component, like the kernel. This +/// limit is currently 64 MiB, which should be sufficient since there is no +/// known device where the entire boot image exceeds this size. +const COMPONENT_MAX_SIZE: u32 = 64 * 1024 * 1024; +/// Maximum size of the bootconfig component in vendor v4 images. This limit is +/// currently 1 KiB, which is ~25x the size of the Pixel 7 Pro stock image's +/// bootconfig. +const BOOTCONFIG_MAX_SIZE: u32 = 1024; + #[derive(Debug, Error)] pub enum Error { #[error("Unknown boot image format")] @@ -71,8 +80,8 @@ pub enum Error { WriteFieldError(&'static str, #[source] io::Error), #[error("{0:?} field: invalid value: {1}")] InvalidFieldValue(&'static str, u32), - #[error("{0:?} field exceeds integer bounds")] - IntegerTooLarge(&'static str), + #[error("{0:?} field is out of bounds")] + FieldOutOfBounds(&'static str), #[error("Invalid data: {0}")] InvalidData(&'static str), #[error("VTS signature is missing hash descriptor")] @@ -257,6 +266,14 @@ impl FromReader for BootImageV0Through2 { return Err(Error::UnknownHeaderVersion(header_version)); } + if kernel_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("kernel_size")); + } else if ramdisk_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("ramdisk_size")); + } else if second_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("second_size")); + } + let os_version = reader.read_u32::()?; let name = reader @@ -281,6 +298,10 @@ impl FromReader for BootImageV0Through2 { let mut v1_data = if header_version >= 1 { let recovery_dtbo_size = reader.read_u32::()?; + if recovery_dtbo_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("recovery_dtbo_size")); + } + let recovery_dtbo_offset = reader.read_u64::()?; let header_size = reader.read_u32::()?; @@ -305,6 +326,10 @@ impl FromReader for BootImageV0Through2 { let mut v2_data = if header_version == 2 { let dtb_size = reader.read_u32::()?; + if dtb_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("dtb_size")); + } + let dtb_addr = reader.read_u64::()?; let v2_extra = V2Extra { @@ -329,29 +354,39 @@ impl FromReader for BootImageV0Through2 { let mut ramdisk = vec![]; let mut second = vec![]; - kernel.resize(kernel_size.to_usize().unwrap(), 0); - reader.read_exact(&mut kernel)?; + kernel.resize(kernel_size as usize, 0); + reader + .read_exact(&mut kernel) + .map_err(|e| Error::ReadFieldError("kernel", e))?; padding::read_discard(&mut reader, page_size.into())?; - ramdisk.resize(ramdisk_size.to_usize().unwrap(), 0); - reader.read_exact(&mut ramdisk)?; + ramdisk.resize(ramdisk_size as usize, 0); + reader + .read_exact(&mut ramdisk) + .map_err(|e| Error::ReadFieldError("ramdisk", e))?; padding::read_discard(&mut reader, page_size.into())?; - second.resize(second_size.to_usize().unwrap(), 0); - reader.read_exact(&mut second)?; + second.resize(second_size as usize, 0); + reader + .read_exact(&mut second) + .map_err(|e| Error::ReadFieldError("second", e))?; padding::read_discard(&mut reader, page_size.into())?; if let Some(v1) = &mut v1_data { v1.v1_extra .recovery_dtbo - .resize(v1.recovery_dtbo_size.to_usize().unwrap(), 0); - reader.read_exact(&mut v1.v1_extra.recovery_dtbo)?; + .resize(v1.recovery_dtbo_size as usize, 0); + reader + .read_exact(&mut v1.v1_extra.recovery_dtbo) + .map_err(|e| Error::ReadFieldError("recovery_dtbo", e))?; padding::read_discard(&mut reader, page_size.into())?; } if let Some(v2) = &mut v2_data { - v2.v2_extra.dtb.resize(v2.dtb_size.to_usize().unwrap(), 0); - reader.read_exact(&mut v2.v2_extra.dtb)?; + v2.v2_extra.dtb.resize(v2.dtb_size as usize, 0); + reader + .read_exact(&mut v2.v2_extra.dtb) + .map_err(|e| Error::ReadFieldError("dtb", e))?; padding::read_discard(&mut reader, page_size.into())?; } @@ -381,30 +416,33 @@ impl ToWriter for BootImageV0Through2 { type Error = Error; fn to_writer(&self, writer: W) -> Result<()> { + if self.kernel.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("kernel_size")); + } else if self.ramdisk.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("ramdisk_size")); + } else if self.second.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("second_size")); + } + + if let Some(v1) = &self.v1_extra { + if v1.recovery_dtbo.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("recovery_dtbo_size")); + } + } + if let Some(v2) = &self.v2_extra { + if v2.dtb.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("dtb_size")); + } + } + let mut writer = CountingWriter::new(writer); - let kernel_size = self - .kernel - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("kernel_size"))?; - let ramdisk_size = self - .ramdisk - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("ramdisk_size"))?; - let second_size = self - .second - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("second_size"))?; - writer.write_all(&BOOT_MAGIC)?; - writer.write_u32::(kernel_size)?; + writer.write_u32::(self.kernel.len() as u32)?; writer.write_u32::(self.kernel_addr)?; - writer.write_u32::(ramdisk_size)?; + writer.write_u32::(self.ramdisk.len() as u32)?; writer.write_u32::(self.ramdisk_addr)?; - writer.write_u32::(second_size)?; + writer.write_u32::(self.second.len() as u32)?; writer.write_u32::(self.second_addr)?; writer.write_u32::(self.tags_addr)?; writer.write_u32::(self.page_size)?; @@ -427,46 +465,44 @@ impl ToWriter for BootImageV0Through2 { .map_err(|e| Error::WriteFieldError("extra_cmdline", e))?; if let Some(v1) = &self.v1_extra { - let recovery_dtbo_size = v1 - .recovery_dtbo - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("recovery_dtbo_size"))?; - - writer.write_u32::(recovery_dtbo_size)?; + writer.write_u32::(v1.recovery_dtbo.len() as u32)?; writer.write_u64::(v1.recovery_dtbo_offset)?; writer.write_u32::(self.header_size())?; } if let Some(v2) = &self.v2_extra { - let dtb_size = v2 - .dtb - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("dtb_size"))?; - - writer.write_u32::(dtb_size)?; + writer.write_u32::(v2.dtb.len() as u32)?; writer.write_u64::(v2.dtb_addr)?; } padding::write_zeros(&mut writer, self.page_size.into())?; - writer.write_all(&self.kernel)?; + writer + .write_all(&self.kernel) + .map_err(|e| Error::WriteFieldError("kernel", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; - writer.write_all(&self.ramdisk)?; + writer + .write_all(&self.ramdisk) + .map_err(|e| Error::WriteFieldError("ramdisk", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; - writer.write_all(&self.second)?; + writer + .write_all(&self.second) + .map_err(|e| Error::WriteFieldError("second", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; if let Some(v1) = &self.v1_extra { - writer.write_all(&v1.recovery_dtbo)?; + writer + .write_all(&v1.recovery_dtbo) + .map_err(|e| Error::WriteFieldError("recovery_dtbo", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; } if let Some(v2) = &self.v2_extra { - writer.write_all(&v2.dtb)?; + writer + .write_all(&v2.dtb) + .map_err(|e| Error::WriteFieldError("dtb", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; } @@ -572,12 +608,23 @@ impl FromReader for BootImageV3Through4 { return Err(Error::UnknownHeaderVersion(header_version)); } + if kernel_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("kernel_size")); + } else if ramdisk_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("ramdisk_size")); + } + let cmdline = reader .read_string_padded(BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE) .map_err(|e| Error::ReadFieldError("cmdline", e))?; let signature_size = if header_version == 4 { - Some(reader.read_u32::()?) + let signature_size = reader.read_u32::()?; + if signature_size > HDR_V4_SIGNATURE_SIZE as u32 { + return Err(Error::FieldOutOfBounds("signature_size")); + } + + Some(signature_size) } else { None }; @@ -591,20 +638,26 @@ impl FromReader for BootImageV3Through4 { let mut kernel = vec![]; let mut ramdisk = vec![]; - kernel.resize(kernel_size.to_usize().unwrap(), 0); - reader.read_exact(&mut kernel)?; + kernel.resize(kernel_size as usize, 0); + reader + .read_exact(&mut kernel) + .map_err(|e| Error::ReadFieldError("kernel", e))?; padding::read_discard(&mut reader, PAGE_SIZE.into())?; - ramdisk.resize(ramdisk_size.to_usize().unwrap(), 0); - reader.read_exact(&mut ramdisk)?; + ramdisk.resize(ramdisk_size as usize, 0); + reader + .read_exact(&mut ramdisk) + .map_err(|e| Error::ReadFieldError("ramdisk", e))?; padding::read_discard(&mut reader, PAGE_SIZE.into())?; // Don't preserve the signature. It is only used for VTS tests and is // not relevant for booting. let v4_extra = if let Some(s) = signature_size { // OnePlus images have an invalid signature consisting of all zeros. - let mut data = vec![0u8; s.to_usize().unwrap()]; - reader.read_exact(&mut data)?; + let mut data = vec![0u8; s as usize]; + reader + .read_exact(&mut data) + .map_err(|e| Error::ReadFieldError("signature", e))?; let signature = if s > 0 && !util::is_zero(&data) { Some(Header::from_reader(Cursor::new(data))?) @@ -634,22 +687,17 @@ impl FromReader for BootImageV3Through4 { impl BootImageV3Through4 { fn to_writer_internal(&self, writer: impl Write, skip_v4_sig: bool) -> Result<()> { + if self.kernel.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("kernel_size")); + } else if self.ramdisk.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("ramdisk_size")); + } + let mut writer = CountingWriter::new(writer); - let kernel_size = self - .kernel - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("kernel_size"))?; - let ramdisk_size = self - .ramdisk - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("ramdisk_size"))?; - writer.write_all(&BOOT_MAGIC)?; - writer.write_u32::(kernel_size)?; - writer.write_u32::(ramdisk_size)?; + writer.write_u32::(self.kernel.len() as u32)?; + writer.write_u32::(self.ramdisk.len() as u32)?; writer.write_u32::(self.os_version)?; writer.write_u32::(self.header_size())?; @@ -673,7 +721,7 @@ impl BootImageV3Through4 { // The VTS signature is always a fixed size. if size > HDR_V4_SIGNATURE_SIZE { - return Err(Error::IntegerTooLarge("signature_size")); + return Err(Error::FieldOutOfBounds("signature_size")); } padding::write_zeros(&mut sig_writer, HDR_V4_SIGNATURE_SIZE)?; @@ -688,15 +736,21 @@ impl BootImageV3Through4 { padding::write_zeros(&mut writer, PAGE_SIZE.into())?; - writer.write_all(&self.kernel)?; + writer + .write_all(&self.kernel) + .map_err(|e| Error::WriteFieldError("kernel", e))?; padding::write_zeros(&mut writer, PAGE_SIZE.into())?; - writer.write_all(&self.ramdisk)?; + writer + .write_all(&self.ramdisk) + .map_err(|e| Error::WriteFieldError("ramdisk", e))?; padding::write_zeros(&mut writer, PAGE_SIZE.into())?; if !skip_v4_sig { if let Some(sig) = v4_signature { - writer.write_all(&sig)?; + writer + .write_all(&sig) + .map_err(|e| Error::WriteFieldError("signature", e))?; padding::write_zeros(&mut writer, PAGE_SIZE.into())?; } } @@ -912,7 +966,11 @@ impl FromReader for VendorBootImageV3Through4 { let page_size = reader.read_u32::()?; let kernel_addr = reader.read_u32::()?; let ramdisk_addr = reader.read_u32::()?; + let vendor_ramdisk_size = reader.read_u32::()?; + if vendor_ramdisk_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("vendor_ramdisk_size")); + } let cmdline = reader .read_string_padded(VENDOR_BOOT_ARGS_SIZE) @@ -925,7 +983,12 @@ impl FromReader for VendorBootImageV3Through4 { .map_err(|e| Error::ReadFieldError("name", e))?; let header_size = reader.read_u32::()?; + let dtb_size = reader.read_u32::()?; + if dtb_size > COMPONENT_MAX_SIZE { + return Err(Error::FieldOutOfBounds("dtb_size")); + } + let dtb_addr = reader.read_u64::()?; struct V4Data { @@ -938,7 +1001,11 @@ impl FromReader for VendorBootImageV3Through4 { let table_size = reader.read_u32::()?; let table_entry_num = reader.read_u32::()?; let table_entry_size = reader.read_u32::()?; + let bootconfig_size = reader.read_u32::()?; + if bootconfig_size > BOOTCONFIG_MAX_SIZE { + return Err(Error::FieldOutOfBounds("bootconfig_size")); + } if table_entry_size != VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE { return Err(Error::InvalidFieldValue( @@ -972,8 +1039,10 @@ impl FromReader for VendorBootImageV3Through4 { let mut ramdisks = vec![]; - let mut vendor_ramdisk_data = vec![0u8; vendor_ramdisk_size.to_usize().unwrap()]; - reader.read_exact(&mut vendor_ramdisk_data)?; + let mut vendor_ramdisk_data = vec![0u8; vendor_ramdisk_size as usize]; + reader + .read_exact(&mut vendor_ramdisk_data) + .map_err(|e| Error::ReadFieldError("vendor_ramdisk_data", e))?; padding::read_discard(&mut reader, page_size.into())?; // For v3, this is just one big ramdisk. For v4, we have to wait until @@ -984,8 +1053,10 @@ impl FromReader for VendorBootImageV3Through4 { vendor_ramdisk_data = vec![]; } - let mut dtb = vec![0u8; dtb_size.to_usize().unwrap()]; - reader.read_exact(&mut dtb)?; + let mut dtb = vec![0u8; dtb_size as usize]; + reader + .read_exact(&mut dtb) + .map_err(|e| Error::ReadFieldError("dtb", e))?; padding::read_discard(&mut reader, page_size.into())?; if let Some(v4) = &mut v4_data { @@ -994,6 +1065,10 @@ impl FromReader for VendorBootImageV3Through4 { for _ in 0..v4.vendor_ramdisk_table_entry_num { let ramdisk_size = reader.read_u32::()?; + if ramdisk_size > vendor_ramdisk_size { + return Err(Error::FieldOutOfBounds("ramdisk_size")); + } + let ramdisk_offset = reader.read_u32::()?; let ramdisk_type = reader.read_u32::()?; @@ -1010,7 +1085,7 @@ impl FromReader for VendorBootImageV3Through4 { return Err(Error::InvalidFieldValue("ramdisk_offset", ramdisk_offset)); } - let mut ramdisk = vec![0u8; ramdisk_size.to_usize().unwrap()]; + let mut ramdisk = vec![0u8; ramdisk_size as usize]; ramdisk_reader.read_exact(&mut ramdisk)?; ramdisks.push(ramdisk); @@ -1033,7 +1108,7 @@ impl FromReader for VendorBootImageV3Through4 { padding::read_discard(&mut reader, page_size.into())?; v4.v4_extra.bootconfig = reader - .read_string_padded(v4.bootconfig_size.to_usize().unwrap()) + .read_string_padded(v4.bootconfig_size as usize) .map_err(|e| Error::ReadFieldError("bootconfig", e))?; } @@ -1070,27 +1145,29 @@ impl ToWriter for VendorBootImageV3Through4 { return Err(Error::InvalidData("v3 only supports one ramdisk")); } - let mut writer = CountingWriter::new(writer); + let vendor_ramdisk_size = self.ramdisks.iter().map(|r| r.len()).sum::(); + if vendor_ramdisk_size > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("vendor_ramdisk_size")); + } - let vendor_ramdisk_size = self - .ramdisks - .iter() - .map(|r| r.len()) - .sum::() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("vendor_ramdisk_size"))?; - let dtb_size = self - .dtb - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("dtb_size"))?; + if self.dtb.len() > COMPONENT_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("dtb_size")); + } + + if let Some(v4) = &self.v4_extra { + if v4.bootconfig.len() > BOOTCONFIG_MAX_SIZE as usize { + return Err(Error::FieldOutOfBounds("bootconfig_size")); + } + } + + let mut writer = CountingWriter::new(writer); writer.write_all(&VENDOR_BOOT_MAGIC)?; writer.write_u32::(self.header_version())?; writer.write_u32::(self.page_size)?; writer.write_u32::(self.kernel_addr)?; writer.write_u32::(self.ramdisk_addr)?; - writer.write_u32::(vendor_ramdisk_size)?; + writer.write_u32::(vendor_ramdisk_size as u32)?; writer .write_string_padded(&self.cmdline, VENDOR_BOOT_ARGS_SIZE) @@ -1103,7 +1180,7 @@ impl ToWriter for VendorBootImageV3Through4 { .map_err(|e| Error::WriteFieldError("name", e))?; writer.write_u32::(self.header_size())?; - writer.write_u32::(dtb_size)?; + writer.write_u32::(self.dtb.len() as u32)?; writer.write_u64::(self.dtb_addr)?; if let Some(v4) = &self.v4_extra { @@ -1111,38 +1188,37 @@ impl ToWriter for VendorBootImageV3Through4 { .ramdisks .len() .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("vendor_ramdisk_table_entry_num"))?; + .ok_or_else(|| Error::FieldOutOfBounds("vendor_ramdisk_table_entry_num"))?; let vendor_ramdisk_table_size = vendor_ramdisk_table_entry_num .checked_mul(VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE) .and_then(|v| v.to_u32()) - .ok_or_else(|| Error::IntegerTooLarge("vendor_ramdisk_table_size"))?; - let bootconfig_size = v4 - .bootconfig - .len() - .to_u32() - .ok_or_else(|| Error::IntegerTooLarge("bootconfig"))?; + .ok_or_else(|| Error::FieldOutOfBounds("vendor_ramdisk_table_size"))?; writer.write_u32::(vendor_ramdisk_table_size)?; writer.write_u32::(vendor_ramdisk_table_entry_num)?; writer.write_u32::(VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE)?; - writer.write_u32::(bootconfig_size)?; + writer.write_u32::(v4.bootconfig.len() as u32)?; } padding::write_zeros(&mut writer, self.page_size.into())?; for ramdisk in &self.ramdisks { - writer.write_all(ramdisk)?; + writer + .write_all(ramdisk) + .map_err(|e| Error::WriteFieldError("vendor_ramdisk_data", e))?; } padding::write_zeros(&mut writer, self.page_size.into())?; - writer.write_all(&self.dtb)?; + writer + .write_all(&self.dtb) + .map_err(|e| Error::WriteFieldError("dtb", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; if let Some(v4) = &self.v4_extra { let mut ramdisk_offset = 0; for (ramdisk, meta) in self.ramdisks.iter().zip(&v4.ramdisk_metas) { - let ramdisk_size = ramdisk.len().to_u32().unwrap(); + let ramdisk_size = ramdisk.len() as u32; writer.write_u32::(ramdisk_size)?; writer.write_u32::(ramdisk_offset)?; @@ -1160,7 +1236,9 @@ impl ToWriter for VendorBootImageV3Through4 { } padding::write_zeros(&mut writer, self.page_size.into())?; - writer.write_all(v4.bootconfig.as_bytes())?; + writer + .write_all(v4.bootconfig.as_bytes()) + .map_err(|e| Error::WriteFieldError("bootconfig", e))?; padding::write_zeros(&mut writer, self.page_size.into())?; }