libfmq: Fix from_raw_parts UB when used with empty vectors
`std::slice::from_raw_parts` has undefined behavior when called
with a null pointer and a zero length. This may be the case when the
descriptor vectors are empty as `data()` may return a null pointer.
Add and use a `slice_from_raw_parts_or_empty` function that returns an
empty slice in this case.
Test: atest --host libfmq_rust_unit_test
Change-Id: I4400939879f224c071d41ad646b036cf77b41df4
diff --git a/Android.bp b/Android.bp
index bbd36e6..8b3a6e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -184,8 +184,8 @@
],
}
-rust_library {
- name: "libfmq_rust",
+rust_defaults {
+ name: "libfmq_rust_defaults",
host_supported: true,
vendor_available: true,
product_available: true,
@@ -200,3 +200,16 @@
],
proc_macros: [],
}
+
+rust_library {
+ name: "libfmq_rust",
+ defaults: ["libfmq_rust_defaults"],
+}
+
+rust_test {
+ name: "libfmq_rust_unit_test",
+ defaults: ["libfmq_rust_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/libfmq.rs b/libfmq.rs
index b743df1..47c5c15 100644
--- a/libfmq.rs
+++ b/libfmq.rs
@@ -242,10 +242,18 @@
// Calls to the descFoo accessors on erased_desc are sound because we know inner.dupeDesc
// returns a valid pointer to a new heap-allocated ErasedMessageQueueDesc.
let (grantors, fds, ints, quantum, flags) = unsafe {
- use std::slice::from_raw_parts;
- let grantors = from_raw_parts(descGrantors(erased_desc), descNumGrantors(erased_desc));
- let fds = from_raw_parts(descHandleFDs(erased_desc), descHandleNumFDs(erased_desc));
- let ints = from_raw_parts(descHandleInts(erased_desc), descHandleNumInts(erased_desc));
+ let grantors = slice_from_raw_parts_or_empty(
+ descGrantors(erased_desc),
+ descNumGrantors(erased_desc),
+ );
+ let fds = slice_from_raw_parts_or_empty(
+ descHandleFDs(erased_desc),
+ descHandleNumFDs(erased_desc),
+ );
+ let ints = slice_from_raw_parts_or_empty(
+ descHandleInts(erased_desc),
+ descHandleNumInts(erased_desc),
+ );
let quantum = descQuantum(erased_desc);
let flags = descFlags(erased_desc);
(grantors, fds, ints, quantum, flags)
@@ -290,6 +298,25 @@
}
}
+/// Forms a slice from a pointer and a length.
+///
+/// Returns an empty slice when `data` is a null pointer and `len` is zero.
+///
+/// # Safety
+///
+/// This function has the same safety requirements as [`std::slice::from_raw_parts`],
+/// but unlike that function, does not exhibit undefined behavior when `data` is a
+/// null pointer and `len` is zero. In this case, it returns an empty slice.
+unsafe fn slice_from_raw_parts_or_empty<'a, T>(data: *const T, len: usize) -> &'a [T] {
+ if data.is_null() && len == 0 {
+ &[]
+ } else {
+ // SAFETY: The caller must guarantee to satisfy the safety requirements
+ // of the standard library function [`std::slice::from_raw_parts`].
+ unsafe { std::slice::from_raw_parts(data, len) }
+ }
+}
+
#[inline(always)]
fn ptr<T: Share>(txn: &MemTransaction, idx: usize) -> *mut T {
let (base, region_idx) = if idx < txn.first.length {
@@ -433,3 +460,33 @@
unsafe { self.inner.beginRead(n, addr_of_mut!(txn)) }.then_some(txn)
}
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn slice_from_raw_parts_or_empty_with_nonempty() {
+ const SLICE: &[u8] = &[1, 2, 3, 4, 5, 6];
+ // SAFETY: We are constructing a slice from the pointer and length of valid slice.
+ let from_raw_parts = unsafe {
+ let ptr = SLICE.as_ptr();
+ let len = SLICE.len();
+ slice_from_raw_parts_or_empty(ptr, len)
+ };
+ assert_eq!(SLICE, from_raw_parts);
+ }
+
+ #[test]
+ fn slice_from_raw_parts_or_empty_with_null_pointer_zero_length() {
+ // SAFETY: Calling `slice_from_raw_parts_or_empty` with a null pointer
+ // and a zero length is explicitly allowed by its safety requirements.
+ // In this case, `std::slice::from_raw_parts` has undefined behavior.
+ let empty_from_raw_parts = unsafe {
+ let ptr: *const u8 = std::ptr::null();
+ let len = 0;
+ slice_from_raw_parts_or_empty(ptr, len)
+ };
+ assert_eq!(&[] as &[u8], empty_from_raw_parts);
+ }
+}