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::{AsHandleRef, Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, 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 { Handle::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 = std::mem::ManuallyDrop::new(unsafe { Handle::from_raw(handle_info.handle) });
121    handle.basic_info().unwrap().koid.raw_koid()
122}
123
124/// Converts a `HandleDisposition` to a raw `zx_handle_disposition_t`.
125pub fn to_zx_handle_disposition_t(
126    mut hd: HandleDisposition<'_>,
127) -> zx_types::zx_handle_disposition_t {
128    match hd.take_op() {
129        HandleOp::Move(handle) => zx_types::zx_handle_disposition_t {
130            operation: zx_types::ZX_HANDLE_OP_MOVE,
131            handle: handle.raw_handle(),
132            type_: hd.object_type.into_raw(),
133            rights: hd.rights.bits(),
134            result: hd.result.into_raw(),
135        },
136        HandleOp::Duplicate(handle_ref) => zx_types::zx_handle_disposition_t {
137            operation: zx_types::ZX_HANDLE_OP_DUPLICATE,
138            handle: handle_ref.raw_handle(),
139            type_: hd.object_type.into_raw(),
140            rights: hd.rights.bits(),
141            result: hd.result.into_raw(),
142        },
143    }
144}
145
146/// Returns the result of the `zx_object_get_info` syscall with topic
147/// `ZX_INFO_HANDLE_VALID`. In particular, returns `Status::BAD_HANDLE` if
148/// the handle is dangling because it was already closed or never assigned
149/// to the process in the first place.
150///
151/// This should only be used in a single-threaded process immediately after
152/// a handle is created/closed, since "The kernel is free to re-use the
153/// integer values of closed handles for newly created objects".
154/// https://fuchsia.dev/fuchsia-src/concepts/kernel/handles#invalid_handles_and_handle_reuse
155#[cfg(target_os = "fuchsia")]
156pub fn get_info_handle_valid(handle_info: &zx_types::zx_handle_info_t) -> Result<(), Status> {
157    use zx::sys;
158    Status::ok(unsafe {
159        sys::zx_object_get_info(
160            handle_info.handle,
161            sys::ZX_INFO_HANDLE_VALID,
162            std::ptr::null_mut(),
163            0,
164            std::ptr::null_mut(),
165            std::ptr::null_mut(),
166        )
167    })
168}
169
170#[cfg(not(target_os = "fuchsia"))]
171pub fn get_info_handle_valid(handle_info: &zx_types::zx_handle_info_t) -> Result<(), Status> {
172    use fidl::EmulatedHandleRef;
173    // Safety: The `from_raw` method is only unsafe because it can lead to
174    // handles being double-closed if used incorrectly. We wrap it in
175    // ManuallyDrop to prevent closing the handle.
176    let handle = std::mem::ManuallyDrop::new(unsafe { Handle::from_raw(handle_info.handle) });
177    // Match the behavior of the syscall, returning BAD_HANDLE if the handle is
178    // the special "never a valid handle" ZX_HANDLE_INVALID, or if it is invalid
179    // because it was closed or never assigned in the first place (dangling).
180    if handle.is_invalid() || handle.is_dangling() {
181        Err(Status::BAD_HANDLE)
182    } else {
183        Ok(())
184    }
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}