fdf/
message.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A helper for managing a self-contained arena-allocated buffer along with its arena handle.
6
7use crate::{Arena, ArenaBox, MixedHandle};
8use core::marker::PhantomData;
9use core::mem::MaybeUninit;
10use core::ops::{Deref, DerefMut};
11use core::ptr::NonNull;
12
13use fdf_sys::*;
14
15/// A struct that holds both an arena along with a data buffer that is allocated within that arena.
16#[derive(Debug)]
17pub struct Message<T: ?Sized + 'static> {
18    data: Option<ArenaBox<'static, T>>,
19    // note: `[Option<MixedHandle>]` is byte-equivalent to a C array of `fdf_handle_t`.
20    handles: Option<ArenaBox<'static, [Option<MixedHandle>]>>,
21    // note: this must maintain its position as the last item of the struct
22    // to ensure that it is freed after the data and handle pointers.
23    arena: Arena,
24    _p: PhantomData<T>,
25}
26
27impl<T: ?Sized> Message<T> {
28    /// Consumes the given arena, data buffer, and handles buffers and returns a message that holds
29    /// all three.
30    ///
31    /// # Panics
32    ///
33    /// This function panics if either of the [`ArenaBox`]s are not allocated by [`Arena`].
34    pub fn new<'a>(
35        arena: &'a Arena,
36        data: Option<ArenaBox<'a, T>>,
37        handles: Option<ArenaBox<'a, [Option<MixedHandle>]>>,
38    ) -> Self {
39        let data = data.map(|data| {
40            assert!(
41                arena.contains(&data),
42                "Data buffer pointer is not in the arena being included in the message"
43            );
44            // SAFETY: we will store this ArenaBox with a clone of the Arena that
45            // owns it.
46            unsafe { ArenaBox::erase_lifetime(data) }
47        });
48        let handles = handles.map(|handles| {
49            assert!(
50                arena.contains(&handles),
51                "Handle buffer pointer is not in the arena being included in the message"
52            );
53            // SAFETY: we will store this ArenaBox with a clone of the Arena that
54            // owns it.
55            unsafe { ArenaBox::erase_lifetime(handles) }
56        });
57        // SAFETY: We just checked that both boxes were allocated from the arena.
58        unsafe { Self::new_unchecked(arena.clone(), data, handles) }
59    }
60
61    /// Given the [`Arena`], allocates a new [`Message`] and runs the given `f` to allow the caller
62    /// to allocate data and handles into the [`Message`] without requiring a check that the
63    /// correct [`Arena`] was used to allocate the data.
64    ///
65    /// Note that it may be possible to sneak an `ArenaBox<'static, T>` into this [`Message`]
66    /// object with this function by using an [`Arena`] with static lifetime to allocate it. This
67    /// will not cause any unsoundness, but if you try to send that message through a [`Channel`]
68    /// it will cause a runtime error when the arenas don't match.
69    pub fn new_with<F>(arena: Arena, f: F) -> Self
70    where
71        F: for<'a> FnOnce(
72            &'a Arena,
73        )
74            -> (Option<ArenaBox<'a, T>>, Option<ArenaBox<'a, [Option<MixedHandle>]>>),
75    {
76        let (data, handles) = f(&arena);
77        // SAFETY: The `for<'a>` in the callback definition makes it so that the caller must
78        // (without resorting to unsafe themselves) allocate the [`ArenaBox`] from the given
79        // [`Arena`].
80        Self {
81            data: data.map(|data| unsafe { ArenaBox::erase_lifetime(data) }),
82            handles: handles.map(|handles| unsafe { ArenaBox::erase_lifetime(handles) }),
83            arena,
84            _p: PhantomData,
85        }
86    }
87
88    /// A shorthand for [`Self::new_with`] when there's definitely a data body and nothing else.
89    pub fn new_with_data<F>(arena: Arena, f: F) -> Self
90    where
91        F: for<'a> FnOnce(&'a Arena) -> ArenaBox<'a, T>,
92    {
93        // SAFETY: The `for<'a>` in the callback definition makes it so that the caller must
94        // (without resorting to unsafe themselves) allocate the [`ArenaBox`] from the given
95        // [`Arena`].
96        let data = Some(unsafe { ArenaBox::erase_lifetime(f(&arena)) });
97        Self { data, handles: None, arena, _p: PhantomData }
98    }
99
100    /// As with [`Self::new`], this consumes the arguments to produce a message object that holds
101    /// all of them together to be extracted again later, but does not validate that the pointers
102    /// came from the same arena.
103    ///
104    /// # Safety
105    ///
106    /// The caller is responsible for:
107    /// - ensuring that the [`ArenaBox`]es came from the same arena as is being passed in to this
108    /// function, or the erased lifetime of the arena boxes might cause use-after-free.
109    /// - the bytes in `data` are actually of type `T`, and are properly aligned for type `T`.
110    pub(crate) unsafe fn new_unchecked(
111        arena: Arena,
112        data_ptr: Option<ArenaBox<'static, T>>,
113        handles_ptr: Option<ArenaBox<'static, [Option<MixedHandle>]>>,
114    ) -> Self {
115        Self { arena, data: data_ptr, handles: handles_ptr, _p: PhantomData }
116    }
117
118    /// Gets a reference to the arena this message was allocated with.
119    pub fn arena(&self) -> &Arena {
120        &self.arena
121    }
122
123    /// Takes the arena and drops any data or handle bodies held in this message
124    pub fn take_arena(self) -> Arena {
125        self.arena
126    }
127
128    /// Gets a reference to the data in this message, if there is any
129    pub fn data(&self) -> Option<&T> {
130        self.data.as_ref().map(ArenaBox::deref)
131    }
132
133    /// Gets a mutable reference to the data in this message, if there is any
134    pub fn data_mut(&mut self) -> Option<&mut T> {
135        self.data.as_mut().map(ArenaBox::deref_mut)
136    }
137
138    /// Maps the message data to a new [`ArenaBox`] based on the arena and the old data.
139    pub fn map_data<F, R: ?Sized>(self, f: F) -> Message<R>
140    where
141        F: for<'a> FnOnce(&'a Arena, ArenaBox<'a, T>) -> ArenaBox<'a, R>,
142    {
143        let Self { arena, data: data_ptr, handles: handles_ptr, .. } = self;
144        let data_ptr = data_ptr.map(|data_ptr| {
145            // SAFETY: The `ArenaBox` being returned is tied to the lifetime
146            // of the arena we gave the closure, and we will now be moving
147            // into the new `Message`. So just like the old one,
148            // the new box is tied to the life of the message and the arena
149            // within it.
150            unsafe { ArenaBox::erase_lifetime(f(&arena, data_ptr)) }
151        });
152        Message { arena, data: data_ptr, handles: handles_ptr, _p: PhantomData }
153    }
154
155    /// Gets a reference to the handles array in this message, if there is one.
156    pub fn handles(&self) -> Option<&[Option<MixedHandle>]> {
157        self.handles.as_ref().map(ArenaBox::deref)
158    }
159
160    /// Gets a mutable reference to the handles array in this message, if there is one.
161    pub fn handles_mut(&mut self) -> Option<&mut [Option<MixedHandle>]> {
162        self.handles.as_mut().map(ArenaBox::deref_mut)
163    }
164
165    /// Gets a reference to all three of the arena, data, and handles of the message
166    pub fn as_refs(&self) -> (&Arena, Option<&T>, Option<&[Option<MixedHandle>]>) {
167        (
168            &self.arena,
169            self.data.as_ref().map(ArenaBox::deref),
170            self.handles.as_ref().map(ArenaBox::deref),
171        )
172    }
173
174    /// Gets a reference to the arena and mutable references to the data handles of the message
175    pub fn as_mut_refs(&mut self) -> (&Arena, Option<&mut T>, Option<&mut [Option<MixedHandle>]>) {
176        (
177            &self.arena,
178            self.data.as_mut().map(ArenaBox::deref_mut),
179            self.handles.as_mut().map(ArenaBox::deref_mut),
180        )
181    }
182
183    /// Unpacks the arena and buffers in this message to the caller.
184    ///
185    /// The `arena` argument provides a place to put the [`Arena`] from this message
186    /// in the local lifetime of the caller so that the [`ArenaBox`]es can be tied to
187    /// its lifetime.
188    pub fn into_arena_boxes<'a>(
189        self,
190        arena: &'a mut Option<Arena>,
191    ) -> (Option<ArenaBox<'a, T>>, Option<ArenaBox<'a, [Option<MixedHandle>]>>) {
192        arena.replace(self.arena);
193        // SAFETY: the lifetime we're giving these [`ArenaBox`]es is the same one
194        // as the lifetime of the place we're putting the [`Arena`] they belong to.
195        let data = self.data.map(|ptr| unsafe { ArenaBox::erase_lifetime(ptr) });
196        let handles = self.handles.map(|ptr| unsafe { ArenaBox::erase_lifetime(ptr) });
197        (data, handles)
198    }
199
200    /// Takes the `ArenaBox`es for the data and handles from this [`Message`], but leaves
201    /// the [`Arena`] in the [`Message`] to act as a holder of the arena lifetime.
202    pub fn take_arena_boxes(
203        &mut self,
204    ) -> (&Arena, Option<ArenaBox<'_, T>>, Option<ArenaBox<'_, [Option<MixedHandle>]>>) {
205        (&self.arena, self.data.take(), self.handles.take())
206    }
207
208    /// Unpacks the arena and buffers into raw pointers
209    ///
210    /// Care must be taken to ensure that the data and handle pointers are not used
211    /// if the arena is freed. If they are never reconstituted into a [`Message`]
212    /// or an [`Arena`] and [`ArenaBox`]es, they will be leaked.
213    pub fn into_raw(
214        self,
215    ) -> (NonNull<fdf_arena_t>, Option<NonNull<T>>, Option<NonNull<[Option<MixedHandle>]>>) {
216        let arena = self.arena.into_raw();
217        // SAFETY: the arena and the pointers we're returning will all have the same
218        // effectively 'static lifetime, and it is up to the caller to make sure that
219        // they free them in the correct order.
220        let data = self.data.map(|data| unsafe { ArenaBox::into_ptr(data) });
221        let handles = self.handles.map(|handles| unsafe { ArenaBox::into_ptr(handles) });
222        (arena, data, handles)
223    }
224}
225
226impl<T> Message<T> {
227    /// Takes the data from the message, dropping the [`Arena`] and handles
228    /// array in the process.
229    pub fn take_data(self) -> Option<T> {
230        self.data.map(ArenaBox::take)
231    }
232
233    /// Takes the data from the message, dropping the [`Arena`] and handles
234    /// array in the process.
235    pub fn take_data_boxed(self) -> Option<Box<T>> {
236        self.data.map(ArenaBox::take_boxed)
237    }
238}
239
240impl<T> Message<[T]> {
241    /// Takes the data from the message, dropping the [`Arena`] and handles
242    /// array in the process.
243    pub fn take_data_boxed_slice(self) -> Option<Box<[T]>> {
244        self.data.map(ArenaBox::take_boxed_slice)
245    }
246}
247
248impl<T> Message<MaybeUninit<T>> {
249    /// Assumes the contents of the data payload of this message are initialized.
250    ///
251    /// # Safety
252    ///
253    /// The caller is responsible for ensuring that the value is initialized
254    /// properly. See [`MaybeUninit::assume_init`] for more details on the
255    /// safety requirements of this.
256    pub unsafe fn assume_init(self) -> Message<T> {
257        // SAFETY: the caller is responsible for ensuring the contents
258        // of the data pointer are initialized.
259        self.map_data(|_, data_ptr| unsafe { ArenaBox::assume_init(data_ptr) })
260    }
261}
262
263impl<T> Message<[MaybeUninit<T>]> {
264    /// Assumes the contents of the data payload of this message are initialized.
265    ///
266    /// # Safety
267    ///
268    /// The caller is responsible for ensuring that the value is initialized
269    /// properly. See [`MaybeUninit::assume_init`] for more details on the
270    /// safety requirements of this.
271    pub unsafe fn assume_init(self) -> Message<[T]> {
272        // SAFETY: the caller is responsible for ensuring the contents
273        // of the data pointer are initialized.
274        self.map_data(|_, data_ptr| unsafe { ArenaBox::assume_init_slice(data_ptr) })
275    }
276}
277
278impl Message<[MaybeUninit<u8>]> {
279    /// Transforms the message body into a message of type `T`
280    ///
281    /// # Safety
282    ///
283    /// The caller is responsible for ensuring that the data portion of this
284    /// message originated from a source with a properly allocated `T` with correct
285    /// alignment
286    pub unsafe fn cast_unchecked<T>(self) -> Message<T> {
287        // SAFETY: the caller is responsible for ensuring the contents
288        // of the data pointer are an initialized value of `T` and are
289        // correctly aligned.
290        self.map_data(|_, data_ptr| unsafe { ArenaBox::cast_unchecked(data_ptr) })
291    }
292}
293
294#[cfg(test)]
295mod test {
296    use zx::HandleBased;
297
298    use super::*;
299    use crate::*;
300
301    #[test]
302    #[should_panic]
303    fn bad_data_pointer() {
304        let arena = Arena::new();
305        let other_arena = Arena::new();
306        Message::new(&arena, Some(other_arena.insert(1)), None);
307    }
308
309    #[test]
310    #[should_panic]
311    fn bad_handle_pointer() {
312        let arena = Arena::new();
313        let other_arena = Arena::new();
314        Message::<()>::new(&arena, None, Some(other_arena.insert_boxed_slice(Box::new([]))));
315    }
316
317    #[test]
318    fn round_trip_data() {
319        let arena = Arena::new();
320        let data = arena.insert(1);
321        let message = Message::new(&arena, Some(data), None);
322        let mut arena = None;
323        let (data, _) = message.into_arena_boxes(&mut arena);
324        assert_eq!(*data.unwrap(), 1);
325    }
326
327    #[test]
328    fn round_trip_handles() {
329        let arena = Arena::new();
330        let zircon_handle = MixedHandle::from_zircon_handle(zx::Port::create().into_handle());
331        let (driver_handle1, driver_handle2) = Channel::create();
332        driver_handle2
333            .write(Message::new_with_data(arena.clone(), |arena| arena.insert(1)))
334            .unwrap();
335
336        let handles = arena
337            .insert_boxed_slice(Box::new([zircon_handle, Some(MixedHandle::from(driver_handle1))]));
338        let message = Message::<()>::new(&arena, None, Some(handles));
339
340        let mut arena = None;
341        let (_, Some(mut handles)) = message.into_arena_boxes(&mut arena) else {
342            panic!("didn't get handles back");
343        };
344        assert_eq!(handles.len(), 2);
345        let MixedHandleType::Zircon(_zircon_handle) = handles[0].take().unwrap().resolve() else {
346            panic!("first handle in the handle set wasn't a zircon handle");
347        };
348        let MixedHandleType::Driver(driver_handle1) = handles[1].take().unwrap().resolve() else {
349            panic!("second handle in the handle set wasn't a driver handle");
350        };
351        let driver_handle1 = unsafe { Channel::<i32>::from_driver_handle(driver_handle1) };
352        assert_eq!(driver_handle1.try_read().unwrap().unwrap().data().unwrap(), &1);
353    }
354
355    #[test]
356    fn map_data() {
357        let arena = Arena::new();
358        let data = arena.insert(1);
359        let message = Message::new(&arena, Some(data), None);
360        let message = message.map_data(|arena, i| arena.insert(*i + 1));
361        assert_eq!(message.data().unwrap(), &2);
362    }
363}