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        munge!(let Self { encoded } = out);
132        encoded.write(wire::Uint32(u32::MAX));
133    }
134
135    /// Returns whether the underlying u32 is invalid.
136    pub fn is_invalid(&self) -> bool {
137        self.as_raw_handle() == 0
138    }
139
140    pub fn invalidate(&mut self) {
141        self.decoded = 0;
142    }
143
144    /// Returns the underlying `u1`.
145    #[inline]
146    pub fn as_raw_handle(&self) -> u32 {
147        unsafe { self.decoded }
148    }
149
150    /// Takes the raw handle out of the handle table.
151    pub(crate) fn take_handle(&mut self) -> crate::Handle {
152        // SAFETY: `WireHandle` is always a valid index into the association table,
153        // and the handle value is always in the association table.
154        unsafe {
155            let pos = self.decoded as usize;
156            self.decoded = 0;
157            let Some(pos) = pos.checked_sub(1) else {
158                return crate::Handle::invalid();
159            };
160            let (id, ptr) = {
161                let table = HANDLE_CLIENT_ASSOC.read();
162                let entry = &table[pos];
163                // We have to read the hid first as when we swap out the client
164                // that is when we mark the slot free.
165                let hid = *entry.hid.get();
166                let ptr = entry.client.swap(std::ptr::null_mut(), Ordering::Release);
167                (hid, ptr)
168            };
169
170            // The pointer should never be null here, as a non-null client was
171            // stored when the Handle was created.
172            assert!(!ptr.is_null(), "Attempted to take an invalid or already taken handle slot");
173            let client = std::sync::Weak::from_raw(ptr);
174
175            crate::Handle { id, client }
176        }
177    }
178}
179
180impl fmt::Debug for Handle {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        self.as_raw_handle().fmt(f)
183    }
184}
185
186unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for Handle {
187    fn decode(
188        mut slot: Slot<'_, Self>,
189        decoder: &mut D,
190        _: <Self as Constrained>::Constraint,
191    ) -> Result<(), DecodeError> {
192        munge!(let Self { encoded } = slot.as_mut());
193
194        match **encoded {
195            0 => (),
196            u32::MAX => {
197                let handle = decoder.take_raw_handle()?;
198                munge!(let Self { mut decoded } = slot);
199                decoded.write(handle);
200            }
201            e => return Err(DecodeError::InvalidHandlePresence(e)),
202        }
203        Ok(())
204    }
205}
206
207/// An optional Zircon handle.
208#[derive(Debug)]
209#[repr(transparent)]
210pub struct OptionalHandle {
211    pub(crate) handle: Handle,
212}
213
214impl Constrained for OptionalHandle {
215    type Constraint = ();
216
217    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
218        Ok(())
219    }
220}
221
222unsafe impl Wire for OptionalHandle {
223    type Narrowed<'de> = Self;
224
225    #[inline]
226    fn zero_padding(out: &mut MaybeUninit<Self>) {
227        munge!(let Self { handle } = out);
228        Handle::zero_padding(handle);
229    }
230}
231
232impl OptionalHandle {
233    /// Encodes a handle as present in a slot.
234    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
235        munge!(let Self { handle } = out);
236        Handle::set_encoded_present(handle);
237    }
238
239    /// Encodes a handle as absent in an output.
240    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
241        munge!(let Self { handle: Handle { encoded } } = out);
242        encoded.write(wire::Uint32(0));
243    }
244
245    /// Returns whether a handle is present.
246    pub fn is_some(&self) -> bool {
247        !self.handle.is_invalid()
248    }
249
250    /// Returns whether a handle is absent.
251    pub fn is_none(&self) -> bool {
252        self.handle.is_invalid()
253    }
254
255    /// Returns the underlying [`zx_handle_t`], if any.
256    #[inline]
257    pub fn as_raw_handle(&self) -> Option<u32> {
258        self.is_some().then(|| self.handle.as_raw_handle())
259    }
260}
261
262unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for OptionalHandle {
263    fn decode(
264        mut slot: Slot<'_, Self>,
265        decoder: &mut D,
266        constraint: <Self as Constrained>::Constraint,
267    ) -> Result<(), DecodeError> {
268        munge!(let Self { handle } = slot.as_mut());
269        Handle::decode(handle, decoder, constraint)
270    }
271}
272
273unsafe impl<E: HandleEncoder + ?Sized> Encode<Handle, E> for crate::Handle {
274    fn encode(
275        self,
276        encoder: &mut E,
277        out: &mut MaybeUninit<Handle>,
278        _: (),
279    ) -> Result<(), EncodeError> {
280        if self.client.upgrade().is_none() {
281            Err(EncodeError::InvalidRequiredHandle)
282        } else {
283            encoder.push_handle(self)?;
284            Handle::set_encoded_present(out);
285            Ok(())
286        }
287    }
288}
289
290impl FromWire<Handle> for crate::Handle {
291    fn from_wire(mut wire: Handle) -> Self {
292        wire.take_handle()
293    }
294}
295
296unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<OptionalHandle, E> for crate::Handle {
297    fn encode_option(
298        this: Option<Self>,
299        encoder: &mut E,
300        out: &mut MaybeUninit<OptionalHandle>,
301        _: (),
302    ) -> Result<(), EncodeError> {
303        if let Some(handle) = this {
304            encoder.push_handle(handle)?;
305            OptionalHandle::set_encoded_present(out);
306        } else {
307            OptionalHandle::set_encoded_absent(out);
308        }
309        Ok(())
310    }
311}
312
313impl FromWireOption<OptionalHandle> for crate::Handle {
314    fn from_wire_option(mut wire: OptionalHandle) -> Option<Self> {
315        if wire.handle.is_invalid() { None } else { Some(wire.handle.take_handle()) }
316    }
317}