Skip to main content

fdomain_client/fidl_next/wire/
wire_handle.rs

1// Copyright 2025 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
5use fidl_next::ValidationError;
6use fuchsia_sync::RwLock;
7use std::cell::UnsafeCell;
8use std::fmt;
9use std::mem::MaybeUninit;
10use std::sync::atomic::{AtomicPtr, Ordering};
11
12use fidl_next_codec::{
13    Constrained, Decode, DecodeError, Encode, EncodeError, EncodeOption, FromWire, FromWireOption,
14    Slot, Wire, munge, wire,
15};
16
17use crate::Client;
18use crate::fidl_next::{HandleDecoder, HandleEncoder};
19
20struct HandleAssoc {
21    hid: UnsafeCell<u32>,
22    client: AtomicPtr<Client>,
23}
24
25// SAFETY: We use the atomic pointer to synchronize access to the hid field.
26unsafe impl Send for HandleAssoc {}
27unsafe impl Sync for HandleAssoc {}
28
29const HANDLE_CLIENT_ASSOC_START_SIZE: usize = 32;
30static HANDLE_CLIENT_ASSOC: RwLock<&'static [HandleAssoc]> = RwLock::new(&[]);
31
32/// An FDomain handle.
33#[repr(C, align(4))]
34pub union Handle {
35    encoded: wire::Uint32,
36    decoded: u32,
37}
38
39impl From<crate::Handle> for Handle {
40    fn from(mut handle: crate::Handle) -> Handle {
41        let id = handle.id;
42        let client = std::mem::replace(&mut handle.client, std::sync::Weak::new());
43        let ptr = client.into_raw() as *mut Client;
44
45        loop {
46            let table = HANDLE_CLIENT_ASSOC.read();
47
48            for (got_id, entry) in table.iter().enumerate() {
49                let got_id: u32 = got_id.try_into().expect("Handle table overflowed u32");
50                if entry
51                    .client
52                    .compare_exchange(
53                        std::ptr::null_mut(),
54                        ptr,
55                        Ordering::Acquire,
56                        Ordering::Relaxed,
57                    )
58                    .is_ok()
59                {
60                    // SAFETY: If we were able to populate the client field then
61                    // we own this slot and it is ours to write.
62                    unsafe {
63                        *entry.hid.get() = id;
64                        return Handle { decoded: got_id + 1 };
65                    }
66                }
67            }
68
69            std::mem::drop(table);
70            let mut table = HANDLE_CLIENT_ASSOC.write();
71            let new_len = std::cmp::max(table.len() * 2, HANDLE_CLIENT_ASSOC_START_SIZE);
72
73            let mut new_vec = Vec::with_capacity(new_len);
74            for entry in table.iter() {
75                // SAFETY: We are holding a write lock on `HANDLE_CLIENT_ASSOC`,
76                // ensuring exclusive access to the table. The `hid` field is
77                // written once when the client pointer is successfully
78                // aclaimed, and by the time we are resizing, any `hid` values
79                // associated with a non-null client pointer are stable.
80                let hid = unsafe { *entry.hid.get() };
81                let client_ptr = entry.client.load(Ordering::Relaxed);
82                new_vec.push(HandleAssoc {
83                    hid: UnsafeCell::new(hid),
84                    client: AtomicPtr::new(client_ptr),
85                });
86            }
87            new_vec.resize_with(new_len, || HandleAssoc {
88                hid: UnsafeCell::new(0),
89                client: AtomicPtr::new(std::ptr::null_mut()),
90            });
91
92            let new = new_vec.into_boxed_slice();
93            let new = Box::leak(new);
94            let old = std::mem::replace(&mut *table, new);
95
96            if old.len() > 0 {
97                // SAFETY: If this isn't the zero-length starting slice then it was
98                // leaked just above in a previous call/iteration.
99                unsafe { drop(Box::from_raw(old as *const [HandleAssoc] as *mut [HandleAssoc])) }
100            }
101        }
102    }
103}
104
105impl Drop for Handle {
106    fn drop(&mut self) {
107        drop(self.take_handle());
108    }
109}
110
111impl Constrained for Handle {
112    type Constraint = ();
113
114    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
115        Ok(())
116    }
117}
118
119unsafe impl Wire for Handle {
120    type Narrowed<'de> = Self;
121
122    #[inline]
123    fn zero_padding(_: &mut MaybeUninit<Self>) {
124        // Wire handles have no padding
125    }
126}
127
128impl Handle {
129    /// Encodes a handle as present in an output.
130    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
131        // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<Handle>`.
132        // Destructuring it via `munge!` only projects a pointer to `MaybeUninit<Uint32>`
133        // and does not read uninitialized memory.
134        let encoded = unsafe {
135            munge!(let Self { encoded } = out);
136            encoded
137        };
138        encoded.write(wire::Uint32(u32::MAX));
139    }
140
141    /// Returns whether the underlying u32 is invalid.
142    pub fn is_invalid(&self) -> bool {
143        self.as_raw_handle() == 0
144    }
145
146    pub fn invalidate(&mut self) {
147        self.decoded = 0;
148    }
149
150    /// Returns the underlying `u1`.
151    #[inline]
152    pub fn as_raw_handle(&self) -> u32 {
153        unsafe { self.decoded }
154    }
155
156    /// Takes the raw handle out of the handle table.
157    pub(crate) fn take_handle(&mut self) -> crate::Handle {
158        // SAFETY: `WireHandle` is always a valid index into the association table,
159        // and the handle value is always in the association table.
160        unsafe {
161            let pos = self.decoded as usize;
162            self.decoded = 0;
163            let Some(pos) = pos.checked_sub(1) else {
164                return crate::Handle::invalid();
165            };
166            let (id, ptr) = {
167                let table = HANDLE_CLIENT_ASSOC.read();
168                let entry = &table[pos];
169                // We have to read the hid first as when we swap out the client
170                // that is when we mark the slot free.
171                let hid = *entry.hid.get();
172                let ptr = entry.client.swap(std::ptr::null_mut(), Ordering::Release);
173                (hid, ptr)
174            };
175
176            // The pointer should never be null here, as a non-null client was
177            // stored when the Handle was created.
178            assert!(!ptr.is_null(), "Attempted to take an invalid or already taken handle slot");
179            let client = std::sync::Weak::from_raw(ptr);
180
181            crate::Handle { id, client }
182        }
183    }
184}
185
186impl fmt::Debug for Handle {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        self.as_raw_handle().fmt(f)
189    }
190}
191
192unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for Handle {
193    fn decode(
194        mut slot: Slot<'_, Self>,
195        decoder: &mut D,
196        _: <Self as Constrained>::Constraint,
197    ) -> Result<(), DecodeError> {
198        // SAFETY: `slot` is a valid `Slot` of `Handle`. Both `encoded` and `decoded` are
199        // 4-byte integer types with no invalid bit patterns.
200        let encoded = unsafe {
201            munge!(let Self { encoded } = slot.as_mut());
202            encoded
203        };
204
205        match **encoded {
206            0 => (),
207            u32::MAX => {
208                let handle = decoder.take_raw_handle()?;
209                // SAFETY: `slot` is a valid `Slot` of `Handle`. Destructuring it via `munge!`
210                // to write `decoded` is safe.
211                let mut decoded = unsafe {
212                    munge!(let Self { decoded } = slot);
213                    decoded
214                };
215                decoded.write(handle);
216            }
217            e => return Err(DecodeError::InvalidHandlePresence(e)),
218        }
219        Ok(())
220    }
221}
222
223/// An optional Zircon handle.
224#[derive(Debug)]
225#[repr(transparent)]
226pub struct OptionalHandle {
227    pub(crate) handle: Handle,
228}
229
230impl Constrained for OptionalHandle {
231    type Constraint = ();
232
233    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
234        Ok(())
235    }
236}
237
238unsafe impl Wire for OptionalHandle {
239    type Narrowed<'de> = Self;
240
241    #[inline]
242    fn zero_padding(out: &mut MaybeUninit<Self>) {
243        munge!(let Self { handle } = out);
244        Handle::zero_padding(handle);
245    }
246}
247
248impl OptionalHandle {
249    /// Encodes a handle as present in a slot.
250    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
251        munge!(let Self { handle } = out);
252        Handle::set_encoded_present(handle);
253    }
254
255    /// Encodes a handle as absent in an output.
256    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
257        // SAFETY: `out` is a valid mutable reference to a `MaybeUninit<OptionalHandle>`.
258        // Destructuring it via `munge!` only projects a pointer to `MaybeUninit<Uint32>`
259        // and does not read uninitialized memory.
260        let encoded = unsafe {
261            munge!(let Self { handle: Handle { encoded } } = out);
262            encoded
263        };
264        encoded.write(wire::Uint32(0));
265    }
266
267    /// Returns whether a handle is present.
268    pub fn is_some(&self) -> bool {
269        !self.handle.is_invalid()
270    }
271
272    /// Returns whether a handle is absent.
273    pub fn is_none(&self) -> bool {
274        self.handle.is_invalid()
275    }
276
277    /// Returns the underlying [`zx_handle_t`], if any.
278    #[inline]
279    pub fn as_raw_handle(&self) -> Option<u32> {
280        self.is_some().then(|| self.handle.as_raw_handle())
281    }
282}
283
284unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for OptionalHandle {
285    fn decode(
286        mut slot: Slot<'_, Self>,
287        decoder: &mut D,
288        constraint: <Self as Constrained>::Constraint,
289    ) -> Result<(), DecodeError> {
290        munge!(let Self { handle } = slot.as_mut());
291        Handle::decode(handle, decoder, constraint)
292    }
293}
294
295unsafe impl<E: HandleEncoder + ?Sized> Encode<Handle, E> for crate::Handle {
296    fn encode(
297        self,
298        encoder: &mut E,
299        out: &mut MaybeUninit<Handle>,
300        _: (),
301    ) -> Result<(), EncodeError> {
302        if self.client.upgrade().is_none() {
303            Err(EncodeError::InvalidRequiredHandle)
304        } else {
305            encoder.push_handle(self)?;
306            Handle::set_encoded_present(out);
307            Ok(())
308        }
309    }
310}
311
312impl FromWire<Handle> for crate::Handle {
313    fn from_wire(mut wire: Handle) -> Self {
314        wire.take_handle()
315    }
316}
317
318unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<OptionalHandle, E> for crate::Handle {
319    fn encode_option(
320        this: Option<Self>,
321        encoder: &mut E,
322        out: &mut MaybeUninit<OptionalHandle>,
323        _: (),
324    ) -> Result<(), EncodeError> {
325        if let Some(handle) = this {
326            encoder.push_handle(handle)?;
327            OptionalHandle::set_encoded_present(out);
328        } else {
329            OptionalHandle::set_encoded_absent(out);
330        }
331        Ok(())
332    }
333}
334
335impl FromWireOption<OptionalHandle> for crate::Handle {
336    fn from_wire_option(mut wire: OptionalHandle) -> Option<Self> {
337        if wire.handle.is_invalid() { None } else { Some(wire.handle.take_handle()) }
338    }
339}