gidl_util/
lib.rs

1// Copyright 2020 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//! This crate contains utility functions used in GIDL tests and benchmarks.
6
7use fidl::encoding::{Context, Decode, Decoder, DefaultFuchsiaResourceDialect, TypeMarker};
8use fidl::{
9    AsHandleRef, HandleBased, HandleDisposition, HandleInfo, HandleOp, NullableHandle, Rights,
10};
11use zx_status::Status;
12use zx_types;
13
14/// Handle subtypes that can be created via `create_handles`. Each subtype `X`
15/// corresponds to a `fidl::X` type that implements `HandleBased`.
16pub enum HandleSubtype {
17    Event,
18    Channel,
19}
20
21impl HandleSubtype {
22    fn obj_type(&self) -> zx_types::zx_obj_type_t {
23        match self {
24            HandleSubtype::Event => zx_types::ZX_OBJ_TYPE_EVENT,
25            HandleSubtype::Channel => zx_types::ZX_OBJ_TYPE_CHANNEL,
26        }
27    }
28}
29
30/// Specifies a handle to be created with `create_handles`. Corresponds to
31/// `HandleDef` in //tools/fidl/gidl/ir/test_case.go.
32pub struct HandleDef {
33    pub subtype: HandleSubtype,
34    pub rights: Rights,
35}
36
37/// Creates a vector of raw handles based on `defs`. Panics if creating any of
38/// the handles fails. The caller is responsible for closing the handles.
39pub fn create_handles(defs: &[HandleDef]) -> Vec<zx_types::zx_handle_info_t> {
40    let mut factory: HandleFactory = Default::default();
41    let mut handle_infos = Vec::with_capacity(defs.len());
42    for def in defs {
43        let default_rights_handle = match def.subtype {
44            HandleSubtype::Event => factory.create_event().unwrap().into_handle(),
45            HandleSubtype::Channel => factory.create_channel().unwrap().into_handle(),
46        };
47        handle_infos.push(zx_types::zx_handle_info_t {
48            handle: match def.rights {
49                Rights::SAME_RIGHTS => default_rights_handle,
50                rights => default_rights_handle.replace(rights).unwrap(),
51            }
52            .into_raw(),
53            ty: def.subtype.obj_type(),
54            rights: def.rights.bits(),
55            unused: 0,
56        });
57    }
58    handle_infos
59}
60
61/// HandleFactory creates handles. For handle subtypes that come in pairs, it
62/// stores the second one and returns it on the next call to minimize syscalls.
63#[derive(Default)]
64struct HandleFactory {
65    extra_channel: Option<fidl::Channel>,
66}
67
68// See src/lib/fuchsia-async/src/handle/mod.rs for handle subtypes. The ones
69// marked "Everywhere" are fully emulated on non-Fuchsia, so we can define
70// factory functions that work on all platforms.
71impl HandleFactory {
72    fn create_event(&mut self) -> Result<fidl::Event, Status> {
73        Ok(fidl::Event::create())
74    }
75
76    fn create_channel(&mut self) -> Result<fidl::Channel, Status> {
77        match self.extra_channel.take() {
78            Some(channel) => Ok(channel),
79            None => {
80                let (c1, c2) = fidl::Channel::create();
81                self.extra_channel = Some(c2);
82                Ok(c1)
83            }
84        }
85    }
86}
87
88/// Copies a raw handle into an owned `HandleBased` handle.
89pub fn copy_handle<T: HandleBased>(handle_info: &zx_types::zx_handle_info_t) -> T {
90    // Safety: The `from_raw` method is only unsafe because it can lead to
91    // handles being double-closed if used incorrectly. GIDL-generated code
92    // ensures that handles are only closed once.
93    T::from_handle(unsafe { NullableHandle::from_raw(handle_info.handle) })
94}
95
96/// Copies raw handles from the given indices to a new vector.
97pub fn select_raw_handle_infos(
98    handle_defs: &[zx_types::zx_handle_info_t],
99    indices: &[usize],
100) -> Vec<zx_types::zx_handle_info_t> {
101    indices.iter().map(|&i| handle_defs[i]).collect()
102}
103
104/// Copies raw handles from the given indices to a new vector of owned
105/// `HandleInfo`s. The caller must ensure handles are closed exactly once.
106pub fn select_handle_infos(
107    handle_defs: &[zx_types::zx_handle_info_t],
108    indices: &[usize],
109) -> Vec<HandleInfo> {
110    // Safety: The `from_raw` method is only unsafe because it can lead to
111    // handles being double-closed if used incorrectly. GIDL-generated code
112    // ensures that handles are only closed once.
113    indices.iter().map(|&i| unsafe { HandleInfo::from_raw(handle_defs[i]) }).collect()
114}
115
116/// Gets the koid of a handle from its raw handle info. Panics if the
117/// `zx_object_get_info` syscall fails.
118pub fn get_handle_koid(handle_info: &zx_types::zx_handle_info_t) -> zx_types::zx_koid_t {
119    // Safety: The `from_raw` method is only unsafe because it can lead to
120    // handles being double-closed if used incorrectly. We wrap it in
121    // ManuallyDrop to prevent closing the handle.
122    let handle =
123        std::mem::ManuallyDrop::new(unsafe { NullableHandle::from_raw(handle_info.handle) });
124    handle.basic_info().unwrap().koid.raw_koid()
125}
126
127/// Converts a `HandleDisposition` to a raw `zx_handle_disposition_t`.
128pub fn to_zx_handle_disposition_t(
129    mut hd: HandleDisposition<'_>,
130) -> zx_types::zx_handle_disposition_t {
131    match hd.take_op() {
132        HandleOp::Move(handle) => zx_types::zx_handle_disposition_t {
133            operation: zx_types::ZX_HANDLE_OP_MOVE,
134            handle: handle.raw_handle(),
135            type_: hd.object_type.into_raw(),
136            rights: hd.rights.bits(),
137            result: hd.result.into_raw(),
138        },
139        HandleOp::Duplicate(handle_ref) => zx_types::zx_handle_disposition_t {
140            operation: zx_types::ZX_HANDLE_OP_DUPLICATE,
141            handle: handle_ref.raw_handle(),
142            type_: hd.object_type.into_raw(),
143            rights: hd.rights.bits(),
144            result: hd.result.into_raw(),
145        },
146    }
147}
148
149/// Returns the result of the `zx_object_get_info` syscall with topic
150/// `ZX_INFO_HANDLE_VALID`. In particular, returns `Status::BAD_HANDLE` if
151/// the handle is dangling because it was already closed or never assigned
152/// to the process in the first place.
153///
154/// This should only be used in a single-threaded process immediately after
155/// a handle is created/closed, since "The kernel is free to re-use the
156/// integer values of closed handles for newly created objects".
157/// https://fuchsia.dev/fuchsia-src/concepts/kernel/handles#invalid_handles_and_handle_reuse
158#[cfg(target_os = "fuchsia")]
159pub fn get_info_handle_valid(handle_info: &zx_types::zx_handle_info_t) -> Result<(), Status> {
160    use zx::sys;
161    Status::ok(unsafe {
162        sys::zx_object_get_info(
163            handle_info.handle,
164            sys::ZX_INFO_HANDLE_VALID,
165            std::ptr::null_mut(),
166            0,
167            std::ptr::null_mut(),
168            std::ptr::null_mut(),
169        )
170    })
171}
172
173#[cfg(not(target_os = "fuchsia"))]
174pub fn get_info_handle_valid(handle_info: &zx_types::zx_handle_info_t) -> Result<(), Status> {
175    use fidl::EmulatedHandleRef;
176    // Safety: The `from_raw` method is only unsafe because it can lead to
177    // handles being double-closed if used incorrectly. We wrap it in
178    // ManuallyDrop to prevent closing the handle.
179    let handle =
180        std::mem::ManuallyDrop::new(unsafe { NullableHandle::from_raw(handle_info.handle) });
181    // Match the behavior of the syscall, returning BAD_HANDLE if the handle is
182    // the special "never a valid handle" ZX_HANDLE_INVALID, or if it is invalid
183    // because it was closed or never assigned in the first place (dangling).
184    if handle.is_invalid() || handle.is_dangling() { Err(Status::BAD_HANDLE) } else { Ok(()) }
185}
186
187/// Returns a vector of `value` repeated `len` times.
188pub fn repeat<T: Clone>(value: T, len: usize) -> Vec<T> {
189    std::iter::repeat(value).take(len).collect::<Vec<_>>()
190}
191
192/// Decodes `T` from the given bytes and handles. Panics on failure.
193pub fn decode_value<T: TypeMarker>(
194    context: Context,
195    bytes: &[u8],
196    handles: &mut [HandleInfo],
197) -> T::Owned
198where
199    T::Owned: Decode<T, DefaultFuchsiaResourceDialect>,
200{
201    let mut value = T::Owned::new_empty();
202    Decoder::decode_with_context::<T>(context, bytes, handles, &mut value).expect(
203        "failed decoding for the GIDL decode() function (not a regular decode test failure!)",
204    );
205    value
206}