pvmfw: Refactor to add config::Range
config::Range ensures non-zero length and also derives Copy.
Validation after instantations can be removed.
Bug: N/A
Test: Build and run protected VM
Change-Id: I84f0ca0fd8240d9fc376224e4c28df1fef5ea230
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index d0a6b7f..2451ec9 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -16,6 +16,7 @@
use core::fmt;
use core::mem;
+use core::num::NonZeroUsize;
use core::ops::Range;
use core::result;
use log::{info, warn};
@@ -133,19 +134,6 @@
size: u32,
}
-impl HeaderEntry {
- pub fn as_range(&self) -> Option<Range<usize>> {
- let size = usize::try_from(self.size).unwrap();
- if size != 0 {
- let offset = self.offset.try_into().unwrap();
- // Allow overflows here for the Range to properly describe the entry (validated later).
- Some(offset..(offset + size))
- } else {
- None
- }
- }
-}
-
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Eq, FromBytes, PartialEq)]
pub struct Version {
@@ -161,10 +149,38 @@
}
}
+/// Range with non-empty length.
+#[derive(Debug, Copy, Clone)]
+struct NonEmptyRange {
+ start: usize,
+ size: NonZeroUsize,
+}
+
+impl NonEmptyRange {
+ pub fn new(start: usize, size: usize) -> Option<Self> {
+ // Ensure end() is safe.
+ start.checked_add(size).unwrap();
+
+ Some(Self { start, size: NonZeroUsize::new(size)? })
+ }
+
+ fn end(&self) -> usize {
+ self.start + self.len()
+ }
+
+ fn len(&self) -> usize {
+ self.size.into()
+ }
+
+ fn as_range(&self) -> Range<usize> {
+ self.start..self.end()
+ }
+}
+
#[derive(Debug)]
pub struct Config<'a> {
body: &'a mut [u8],
- ranges: [Option<Range<usize>>; Entry::COUNT],
+ ranges: [Option<NonEmptyRange>; Entry::COUNT],
}
impl<'a> Config<'a> {
@@ -209,14 +225,24 @@
// Validate that we won't get an invalid alignment in the following due to padding to u64.
const_assert_eq!(mem::size_of::<HeaderEntry>() % mem::size_of::<u64>(), 0);
+ // Ensure entries are in the body.
let limits = header.body_lowest_bound()?..total_size;
- let ranges = [
- // TODO: Find a way to do this programmatically even if the trait
- // `core::marker::Copy` is not implemented for `core::ops::Range<usize>`.
- Self::validated_body_range(Entry::Bcc, &header_entries, &limits)?,
- Self::validated_body_range(Entry::DebugPolicy, &header_entries, &limits)?,
- Self::validated_body_range(Entry::VmDtbo, &header_entries, &limits)?,
- ];
+ let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT];
+ for entry in [Entry::Bcc, Entry::DebugPolicy, Entry::VmDtbo] {
+ let Some(header_entry) = header_entries.get(entry as usize) else { continue };
+ let entry_offset = header_entry.offset.try_into().unwrap();
+ let entry_size = header_entry.size.try_into().unwrap();
+ let Some(range) = NonEmptyRange::new(entry_offset, entry_size) else { continue };
+ let range = range.as_range();
+ if !range.is_within(&limits) {
+ return Err(Error::EntryOutOfBounds(entry, range, limits));
+ }
+
+ ranges[entry as usize] = NonEmptyRange::new(
+ entry_offset - limits.start, // is_within() validates safety of this.
+ entry_size,
+ );
+ }
Ok(Self { body, ranges })
}
@@ -237,7 +263,7 @@
let (bcc, rest) = rest.split_at_mut(bcc_end);
let dp = if let Some(dp_range) = dp_range {
- let dp_start = dp_range.start.checked_sub(bcc_range.end).unwrap();
+ let dp_start = dp_range.start.checked_sub(bcc_range.end()).unwrap();
let dp_end = dp_range.len();
let (_, rest) = rest.split_at_mut(dp_start);
let (dp, _) = rest.split_at_mut(dp_end);
@@ -249,28 +275,7 @@
Ok((bcc, dp))
}
- pub fn get_entry_range(&self, entry: Entry) -> Option<Range<usize>> {
- self.ranges[entry as usize].clone()
- }
-
- fn validated_body_range(
- entry: Entry,
- header_entries: &[HeaderEntry],
- limits: &Range<usize>,
- ) -> Result<Option<Range<usize>>> {
- if let Some(header_entry) = header_entries.get(entry as usize) {
- if let Some(r) = header_entry.as_range() {
- return if r.start <= r.end && r.is_within(limits) {
- let start = r.start - limits.start;
- let end = r.end - limits.start;
-
- Ok(Some(start..end))
- } else {
- Err(Error::EntryOutOfBounds(entry, r, limits.clone()))
- };
- }
- }
-
- Ok(None)
+ fn get_entry_range(&self, entry: Entry) -> Option<NonEmptyRange> {
+ self.ranges[entry as usize]
}
}