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.
45use fidl::encoding::{
6 Context, Decode, Decoder, DefaultFuchsiaResourceDialect, Depth, Encode, Encoder,
7 ResourceTypeMarker, TypeMarker, ALLOC_ABSENT_U32, ALLOC_PRESENT_U32,
8};
910use fdf::{Channel, MixedHandle, MixedHandleType};
11use zx::{
12 AsHandleRef, Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, ObjectType, Rights,
13 Status,
14};
1516use std::marker::PhantomData;
1718use crate::endpoints::DriverClientEnd;
1920#[derive(Debug, Clone)]
21pub struct DriverEndpoint<T>(PhantomData<DriverClientEnd<T>>);
2223unsafe impl<T: 'static> TypeMarker for DriverEndpoint<T> {
24type Owned = DriverClientEnd<T>;
2526fn inline_align(_context: Context) -> usize {
274
28}
2930fn inline_size(_context: Context) -> usize {
314
32}
33}
3435impl<T: 'static> ResourceTypeMarker for DriverEndpoint<T> {
36type Borrowed<'a> = DriverClientEnd<T>;
3738fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> {
39 DriverClientEnd(value.0.take(), PhantomData)
40 }
41}
4243impl<T: 'static> Decode<DriverEndpoint<T>, DefaultFuchsiaResourceDialect> for DriverClientEnd<T> {
44fn new_empty() -> Self {
45Self(None, PhantomData)
46 }
4748unsafe fn decode(
49&mut self,
50 decoder: &mut Decoder<'_, DefaultFuchsiaResourceDialect>,
51 offset: usize,
52 _depth: Depth,
53 ) -> fidl::Result<()> {
54match decoder.read_num::<u32>(offset) {
55 ALLOC_PRESENT_U32 => {}
56 ALLOC_ABSENT_U32 => return Err(fidl::Error::NotNullable),
57_ => return Err(fidl::Error::InvalidPresenceIndicator),
58 }
59// Take what the decoder thinks is a NONE-type zircon handle and validate that it's
60 // a driver handle and use that.
61let handle = decoder.take_next_handle(ObjectType::NONE, Rights::empty())?.into_raw();
62let mixed_handle = unsafe { MixedHandle::try_from_raw(handle) };
63match mixed_handle.map(MixedHandle::resolve) {
64None => (),
65Some(MixedHandleType::Driver(driver_handle)) => {
66// SAFETY: we own this handle as the receiver of the fidl message that contained it.
67self.0 = unsafe { Some(Channel::from_driver_handle(driver_handle)) };
68 }
69_ => {
70// The handle was present but not a driver handle, so is not valid.
71return Err(fidl::Error::Invalid);
72 }
73 }
74Ok(())
75 }
76}
7778unsafe impl<T: 'static> Encode<DriverEndpoint<T>, DefaultFuchsiaResourceDialect>
79for DriverClientEnd<T>
80{
81unsafe fn encode(
82self,
83 encoder: &mut Encoder<'_, DefaultFuchsiaResourceDialect>,
84 offset: usize,
85 _depth: Depth,
86 ) -> fidl::Result<()> {
87let Some(channel) = self.0 else {
88return Err(fidl::Error::NotNullable);
89 };
90// SAFETY: we are taking the handle and converting it into what looks like a zircon
91 // handle for the benefit of the fidl encoding library. If something tries to close it,
92 // it will cause an error and leak the driver handle but it will not cause any other
93 // unsoundness.
94let handle =
95unsafe { fidl::Handle::from_raw(channel.into_driver_handle().into_raw().get()) };
9697// SAFETY: the caller is responsible for ensuring that there's enough space for this
98 // value.
99unsafe { encoder.write_num(ALLOC_PRESENT_U32, offset) };
100 encoder.push_next_handle(HandleDisposition::new(
101 HandleOp::Move(handle),
102 ObjectType::NONE,
103 Rights::empty(),
104 Status::OK,
105 ));
106Ok(())
107 }
108}
109110/// Converts the handle to a [`HandleInfo`], with [`DriverHandle`]s being represented as
111/// a valid driver handle but [`ObjectType::NONE`].
112///
113/// This uses [`Handle::basic_info`] to get the object type and rights for a zircon
114/// handle, so may return an error if that fails.
115///
116/// # Safety
117///
118/// The caller is responsible for making sure that the handle is either converted
119/// back to a [`MixedHandle`] or the correct cleanup happens, or an error or leak
120/// may happen when the [`HandleInfo`] is dropped.
121pub unsafe fn mixed_into_handle_info(this: Option<MixedHandle>) -> Result<HandleInfo, Status> {
122use MixedHandleType::*;
123let Some(this) = this else {
124return Ok(HandleInfo::new(Handle::invalid(), ObjectType::NONE, Rights::empty()));
125 };
126match this.resolve() {
127 Zircon(handle) => {
128let basic_info = handle.basic_info()?;
129Ok(HandleInfo::new(handle, basic_info.object_type, basic_info.rights))
130 }
131 Driver(handle) => {
132// SAFETY: we are wrapping the technically invalid handle in a `ManuallyDrop`
133 // to prevent it from being dropped.
134Ok(HandleInfo::new(
135unsafe { Handle::from_raw(handle.into_raw().get()) },
136 ObjectType::NONE,
137 Rights::empty(),
138 ))
139 }
140 }
141}
142143/// Converts a [`HandleDisposition`] to a [`MixedHandle`], relying on the bit pattern of
144/// the handle to correctly identify [`Handle`] vs. [`fdf::DriverHandle`].
145///
146/// # Panics
147///
148/// This panics if the handle's op is not [`zx::HandleOp::Move`]
149pub fn mixed_from_handle_disposition(
150mut handle: HandleDisposition<'static>,
151) -> Option<MixedHandle> {
152use zx::HandleOp::*;
153match handle.take_op() {
154 Move(handle) => MixedHandle::from_zircon_handle(handle),
155 Duplicate(_) => {
156panic!("tried to convert a duplicate HandleDisposition into a driver MixedHandle")
157 }
158 }
159}
160161#[cfg(test)]
162mod tests {
163use fdf::DriverHandle;
164use zx::Port;
165166use super::*;
167168/// Creates a valid `DriverHandle` by creating a driver channel pair and returning one of them.
169fn make_driver_handle() -> DriverHandle {
170let (left, right) = fdf::Channel::<()>::create();
171 drop(right);
172 left.into_driver_handle()
173 }
174175#[test]
176fn driver_handle_info() {
177let handle = MixedHandle::from(make_driver_handle());
178let handle_info = unsafe { mixed_into_handle_info(Some(handle)).unwrap() };
179assert_eq!(handle_info.object_type, ObjectType::NONE);
180assert_eq!(handle_info.rights, Rights::empty());
181// take the handle back to a mixed handle so it gets dropped properly
182MixedHandle::from_zircon_handle(handle_info.handle).unwrap();
183 }
184185#[test]
186fn driver_handle_disposition() {
187let handle_disposition = HandleDisposition::new(
188 HandleOp::Move(unsafe { Handle::from_raw(make_driver_handle().into_raw().get()) }),
189 ObjectType::NONE,
190 Rights::empty(),
191 Status::OK,
192 );
193 mixed_from_handle_disposition(handle_disposition).unwrap();
194 }
195196#[test]
197fn zircon_handle_info() {
198let handle = MixedHandle::from_zircon_handle(Port::create().into()).unwrap();
199let handle_info = unsafe { mixed_into_handle_info(Some(handle)).unwrap() };
200assert_eq!(handle_info.object_type, ObjectType::PORT);
201assert_eq!(
202 handle_info.rights,
203 Rights::DUPLICATE | Rights::TRANSFER | Rights::READ | Rights::WRITE | Rights::INSPECT
204 );
205// take the handle back to a mixed handle so it gets dropped properly
206MixedHandle::from_zircon_handle(handle_info.handle).unwrap();
207 }
208209#[test]
210fn zircon_handle_disposition() {
211let handle_disposition = HandleDisposition::new(
212 HandleOp::Move(Port::create().into()),
213 ObjectType::PORT,
214 Rights::DUPLICATE | Rights::TRANSFER | Rights::READ | Rights::WRITE | Rights::INSPECT,
215 Status::OK,
216 );
217 mixed_from_handle_disposition(handle_disposition).unwrap();
218 }
219}