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