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()
+    })
+}