Skip to main content

fidl_next_codec/wire/fuchsia/
handle.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
5use core::fmt;
6use core::mem::{MaybeUninit, forget};
7
8use fidl_constants::{ALLOC_ABSENT_U32, ALLOC_PRESENT_U32};
9use zx::sys::{ZX_HANDLE_INVALID, zx_handle_t};
10
11use crate::fuchsia::{HandleDecoder, HandleEncoder};
12use crate::{
13    Constrained, Decode, DecodeError, Encode, EncodeError, EncodeOption, FromWire, FromWireOption,
14    IntoNatural, Slot, ValidationError, Wire, munge, wire,
15};
16
17/// TODO(https://fxbug.dev/465766514): remove
18pub type NullableHandle = Handle;
19
20/// A Zircon handle.
21#[repr(C, align(4))]
22pub union Handle {
23    encoded: wire::Uint32,
24    decoded: zx_handle_t,
25}
26
27impl Drop for Handle {
28    fn drop(&mut self) {
29        // SAFETY: `WireHandle` is always a valid `Handle`.
30        let handle = unsafe { zx::NullableHandle::from_raw(self.as_raw_handle()) };
31        drop(handle);
32    }
33}
34
35// TODO: validate handle rights
36impl Constrained for Handle {
37    type Constraint = ();
38
39    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
40        Ok(())
41    }
42}
43
44// SAFETY: `Handle` is a union of `Uint32` and `zx_handle_t`, both of which are 4 bytes.
45// It has a stable layout and no padding.
46unsafe impl Wire for Handle {
47    type Narrowed<'de> = Self;
48
49    #[inline]
50    fn zero_padding(_: &mut MaybeUninit<Self>) {
51        // Wire handles have no padding
52    }
53}
54
55impl Handle {
56    /// Encodes a handle as present in an output.
57    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
58        munge!(let Self { encoded } = out);
59        encoded.write(wire::Uint32(ALLOC_PRESENT_U32));
60    }
61
62    /// Returns whether the underlying `zx_handle_t` is invalid.
63    pub fn is_invalid(&self) -> bool {
64        self.as_raw_handle() == ZX_HANDLE_INVALID
65    }
66
67    /// Returns the underlying [`zx_handle_t`].
68    #[inline]
69    pub fn as_raw_handle(&self) -> zx_handle_t {
70        // SAFETY: `Handle` is a union of `Uint32` and `zx_handle_t`. Reading `decoded` is safe
71        // because both union fields are 4-byte integers (or wrappers thereof) and do not have
72        // invalid bit patterns.
73        unsafe { self.decoded }
74    }
75}
76
77impl fmt::Debug for Handle {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        self.as_raw_handle().fmt(f)
80    }
81}
82
83// SAFETY: If `decode` returns `Ok`, `slot` is guaranteed to contain a valid decoded `Handle`
84// because it has been written with a handle taken from the decoder.
85unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for Handle {
86    fn decode(
87        mut slot: Slot<'_, Self>,
88        decoder: &mut D,
89        _constraint: Self::Constraint,
90    ) -> Result<(), DecodeError> {
91        munge!(let Self { encoded } = slot.as_mut());
92
93        match **encoded {
94            ALLOC_ABSENT_U32 => return Err(DecodeError::RequiredHandleAbsent),
95            ALLOC_PRESENT_U32 => {
96                let handle = decoder.take_raw_handle()?;
97                munge!(let Self { mut decoded } = slot);
98                decoded.write(handle);
99            }
100            e => return Err(DecodeError::InvalidHandlePresence(e)),
101        }
102        Ok(())
103    }
104}
105
106/// TODO(https://fxbug.dev/465766514): remove
107pub type OptionalNullableHandle = OptionalHandle;
108
109/// An optional Zircon handle.
110#[derive(Debug)]
111#[repr(transparent)]
112pub struct OptionalHandle {
113    handle: Handle,
114}
115
116// TODO: validate handle rights
117impl Constrained for OptionalHandle {
118    type Constraint = ();
119
120    fn validate(_: Slot<'_, Self>, _: Self::Constraint) -> Result<(), ValidationError> {
121        Ok(())
122    }
123}
124
125// SAFETY: `OptionalHandle` is a transparent wrapper around `Handle`, which is `Wire`.
126unsafe impl Wire for OptionalHandle {
127    type Narrowed<'de> = Self;
128
129    #[inline]
130    fn zero_padding(out: &mut MaybeUninit<Self>) {
131        munge!(let Self { handle } = out);
132        Handle::zero_padding(handle);
133    }
134}
135
136impl OptionalHandle {
137    /// Encodes a handle as present in a slot.
138    pub fn set_encoded_present(out: &mut MaybeUninit<Self>) {
139        munge!(let Self { handle } = out);
140        Handle::set_encoded_present(handle);
141    }
142
143    /// Encodes a handle as absent in an output.
144    pub fn set_encoded_absent(out: &mut MaybeUninit<Self>) {
145        munge!(let Self { handle: Handle { encoded } } = out);
146        encoded.write(wire::Uint32(ZX_HANDLE_INVALID));
147    }
148
149    /// Returns whether a handle is present.
150    pub fn is_some(&self) -> bool {
151        !self.handle.is_invalid()
152    }
153
154    /// Returns whether a handle is absent.
155    pub fn is_none(&self) -> bool {
156        self.handle.is_invalid()
157    }
158
159    /// Returns the underlying [`zx_handle_t`], if any.
160    #[inline]
161    pub fn as_raw_handle(&self) -> Option<zx_handle_t> {
162        self.is_some().then(|| self.handle.as_raw_handle())
163    }
164}
165
166// SAFETY: If `decode` returns `Ok`, `slot` is guaranteed to contain a valid decoded
167// `OptionalHandle` because it is either left as `ALLOC_ABSENT_U32` (representing `None`) or
168// written with a handle taken from the decoder.
169unsafe impl<D: HandleDecoder + ?Sized> Decode<D> for OptionalHandle {
170    fn decode(mut slot: Slot<'_, Self>, decoder: &mut D, _: ()) -> Result<(), DecodeError> {
171        munge!(let Self { handle: mut wire_handle } = slot.as_mut());
172        munge!(let Handle { encoded } = wire_handle.as_mut());
173
174        match **encoded {
175            ALLOC_ABSENT_U32 => (),
176            ALLOC_PRESENT_U32 => {
177                let handle = decoder.take_raw_handle()?;
178                munge!(let Handle { mut decoded } = wire_handle);
179                decoded.write(handle);
180            }
181            e => return Err(DecodeError::InvalidHandlePresence(e)),
182        }
183        Ok(())
184    }
185}
186
187// SAFETY: `Handle` has no padding, and `encode` initializes the entire 4 bytes of `out`
188// by calling `Handle::set_encoded_present`.
189unsafe impl<E: HandleEncoder + ?Sized> Encode<Handle, E> for zx::NullableHandle {
190    fn encode(
191        self,
192        encoder: &mut E,
193        out: &mut MaybeUninit<Handle>,
194        _constraint: (),
195    ) -> Result<(), EncodeError> {
196        if self.is_invalid() {
197            Err(EncodeError::InvalidRequiredHandle)
198        } else {
199            encoder.push_handle(self)?;
200            Handle::set_encoded_present(out);
201            Ok(())
202        }
203    }
204}
205
206impl FromWire<Handle> for zx::NullableHandle {
207    fn from_wire(wire: Handle) -> Self {
208        // SAFETY: `WireHandle` is always a valid `NullableHandle`.
209        let handle = unsafe { zx::NullableHandle::from_raw(wire.as_raw_handle()) };
210        forget(wire);
211        handle
212    }
213}
214
215impl IntoNatural for Handle {
216    type Natural = zx::NullableHandle;
217}
218
219// SAFETY: `OptionalHandle` has no padding, and `encode_option` initializes the entire 4 bytes
220// of `out` by calling either `set_encoded_present` or `set_encoded_absent`.
221unsafe impl<E: HandleEncoder + ?Sized> EncodeOption<OptionalHandle, E> for zx::NullableHandle {
222    fn encode_option(
223        this: Option<Self>,
224        encoder: &mut E,
225        out: &mut MaybeUninit<OptionalHandle>,
226        _constraint: (),
227    ) -> Result<(), EncodeError> {
228        if let Some(handle) = this {
229            encoder.push_handle(handle)?;
230            OptionalHandle::set_encoded_present(out);
231        } else {
232            OptionalHandle::set_encoded_absent(out);
233        }
234        Ok(())
235    }
236}
237
238impl FromWireOption<OptionalHandle> for zx::NullableHandle {
239    fn from_wire_option(wire: OptionalHandle) -> Option<Self> {
240        let raw_handle = wire.as_raw_handle();
241        forget(wire);
242        // SAFETY: `raw` is a valid handle value from a decoded `OptionalHandle`.
243        // We `forget(wire)` above to prevent double-closing the handle.
244        raw_handle.map(|raw| unsafe { zx::NullableHandle::from_raw(raw) })
245    }
246}
247
248impl IntoNatural for OptionalHandle {
249    type Natural = Option<zx::NullableHandle>;
250}