libfdt: Make Libfdt::string() actually safe
Instead of blindly trusting that the C code has returned a properly
NULL-terminated C string[*], limit the CStr constructor to whatever
bytes are available between the returned pointer and the end of the DT
slice, which hardens the function by:
- Avoiding UB if CStr::from_ptr wasn't passed a valid C string;
- Avoiding OOB in CStr::from_bytes_until_nul by passing a DT sub-slice.
Test: m pvmfw
Test: atest liblibfdt.integration_test
Change-Id: I56daa3360c8556e7e210d2c53fcea1a332268828
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index bb26e53..a89738f 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -215,12 +215,10 @@
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);
- }
+ let bytes =
+ get_slice_from_ptr(self.as_fdt_slice(), ptr.cast()).ok_or(FdtError::Internal)?;
- // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
- Ok(unsafe { CStr::from_ptr(ptr) })
+ CStr::from_bytes_until_nul(bytes).map_err(|_| FdtError::Internal)
}
}
@@ -271,6 +269,10 @@
s.get(offset..offset.checked_add(len)?)
}
+fn get_slice_from_ptr(s: &[u8], p: *const u8) -> Option<&[u8]> {
+ s.get(get_slice_ptr_offset(s, p)?..)
+}
+
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.