libfdt: Move string functions to Libfdt
Move fdt_string() and fdt_get_name() to the Libfdt trait.
Make the pointer-based indexing safer by avoiding treating the pointers
as integers; see feature(strict_provenance) and use the new
implementation in Fdt::get_from_ptr().
Reduce the unsafe blocks to the strict minimum (i.e. the FFI calls).
Test: m pvmfw
Test: atest liblibfdt.integration_test
Change-Id: Icfbc7736e919313d9cf2dc60d3ea5cfff16a8ca5
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index d5013ca..d81c0c1 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -32,6 +32,7 @@
use core::ffi::{c_int, c_void, CStr};
use core::ops::Range;
use cstr::cstr;
+use libfdt::get_slice_at_ptr;
use result::{fdt_err, fdt_err_expect_zero, fdt_err_or_option};
use zerocopy::AsBytes as _;
@@ -218,13 +219,7 @@
/// Returns the node name.
pub fn name(&self) -> Result<&'a CStr> {
- let mut len: c_int = 0;
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
- // function returns valid null terminating string and otherwise returned values are dropped.
- let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
- as *const c_void;
- let len = usize::try_from(fdt_err(len)?).unwrap();
- let name = self.fdt.get_from_ptr(name, len + 1)?;
+ let name = self.fdt.get_name(self.offset)?;
CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
}
@@ -910,20 +905,7 @@
}
fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
- let ptr = ptr as usize;
- let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
- self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
- }
-
- fn string(&self, offset: c_int) -> Result<&CStr> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
- if res.is_null() {
- return Err(FdtError::Internal);
- }
-
- // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
- Ok(unsafe { CStr::from_ptr(res) })
+ get_slice_at_ptr(self.as_fdt_slice(), ptr.cast(), len).ok_or(FdtError::Internal)
}
/// Returns a shared pointer to the device tree.
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index 0614a5e..bb26e53 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -186,6 +186,18 @@
Ok(fdt_err(ret)?.try_into().unwrap())
}
+ /// Safe wrapper around `fdt_get_name()` (C function).
+ fn get_name(&self, node: c_int) -> Result<&[u8]> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ let mut len = 0;
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
+ // function returns valid null terminating string and otherwise returned values are dropped.
+ let name = unsafe { libfdt_bindgen::fdt_get_name(fdt, node, &mut len) };
+ let len = usize::try_from(fdt_err(len)?).unwrap().checked_add(1).unwrap();
+
+ get_slice_at_ptr(self.as_fdt_slice(), name.cast(), len).ok_or(FdtError::Internal)
+ }
+
/// Safe wrapper around `fdt_find_max_phandle()` (C function).
fn find_max_phandle(&self) -> Result<Phandle> {
let fdt = self.as_fdt_slice().as_ptr().cast();
@@ -197,6 +209,19 @@
phandle.try_into()
}
+
+ /// Safe wrapper around `fdt_string()` (C function).
+ fn string(&self, offset: c_int) -> Result<&CStr> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ptr = unsafe { libfdt_bindgen::fdt_string(fdt, offset) };
+ if ptr.is_null() {
+ return Err(FdtError::Internal);
+ }
+
+ // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
+ Ok(unsafe { CStr::from_ptr(ptr) })
+ }
}
/// Wrapper for the read-write libfdt.h functions.
@@ -239,3 +264,17 @@
fdt_err(ret)
}
}
+
+pub(crate) fn get_slice_at_ptr(s: &[u8], p: *const u8, len: usize) -> Option<&[u8]> {
+ let offset = get_slice_ptr_offset(s, p)?;
+
+ s.get(offset..offset.checked_add(len)?)
+}
+
+fn get_slice_ptr_offset(s: &[u8], p: *const u8) -> Option<usize> {
+ s.as_ptr_range().contains(&p).then(|| {
+ // SAFETY: Both pointers are in bounds, derive from the same object, and size_of::<T>()=1.
+ (unsafe { p.offset_from(s.as_ptr()) }) as usize
+ // TODO(stable_feature(ptr_sub_ptr)): p.sub_ptr()
+ })
+}