oc_wasm_safe/descriptor.rs
1//! Descriptors which refer to opaque values such as open file handles.
2//!
3//! The types in this module provide RAII-style wrappers around integer descriptors, which in turn
4//! are used by OC-Wasm to represent opaque values (such as open file handles) that can be returned
5//! by component calls but cannot be represented as pure data in CBOR.
6
7use super::error::{Error, Result};
8use core::fmt::{Debug, Formatter};
9use core::marker::PhantomData;
10use core::mem::forget;
11use core::num::NonZeroU32;
12use minicbor::data::Tag;
13use minicbor::decode::{Decode, Decoder};
14use minicbor::encode::{Encode, Encoder, Write};
15use oc_wasm_sys::descriptor as sys;
16
17/// The Identifier CBOR tag number.
18const IDENTIFIER: Tag = Tag::new(39);
19
20/// CBOR-encodes an opaque value descriptor.
21///
22/// This produces an integer with the Identifier tag.
23fn cbor_encode<W: Write>(
24 descriptor: u32,
25 e: &mut Encoder<W>,
26) -> core::result::Result<(), minicbor::encode::Error<W::Error>> {
27 e.tag(IDENTIFIER)?.u32(descriptor)?;
28 Ok(())
29}
30
31/// A value that can be converted into an opaque value descriptor.
32///
33/// A value implementing this trait holds, borrows, or is otherwise able to provide an opaque value
34/// descriptor as a `u32`.
35pub trait AsRaw {
36 /// Returns the raw descriptor value.
37 #[must_use = "This function is only useful for its return value"]
38 fn as_raw(&self) -> u32;
39}
40
41/// A value that can be borrowed as an opaque value descriptor.
42///
43/// A value implementing this trait is able to produce a [`Borrowed`] value referring to a
44/// descriptor.
45#[allow(clippy::module_name_repetitions)] // This is the best name I could come up with.
46pub trait AsDescriptor {
47 /// Borrows the descriptor.
48 #[must_use = "This function is only useful for its return value"]
49 fn as_descriptor(&self) -> Borrowed<'_>;
50}
51
52/// A value that can be converted into an opaque value descriptor.
53///
54/// A value implementing this trait is able to produce an [`Owned`] value referring to a descriptor
55/// by consuming itself.
56#[allow(clippy::module_name_repetitions)] // This is the best name I could come up with.
57pub trait IntoDescriptor {
58 /// Converts to the descriptor.
59 #[must_use = "This function is only useful for its return value"]
60 fn into_descriptor(self) -> Owned;
61}
62
63/// An owned opaque value descriptor.
64///
65/// A value of this type encapsulates an opaque value descriptor. Cloning it duplicates the
66/// descriptor. Dropping it closes the descriptor. CBOR-encoding it yields an integer with the
67/// Identifier tag.
68#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
69pub struct Owned(NonZeroU32);
70
71impl Owned {
72 /// Wraps a raw integer descriptor in a `Descriptor` object.
73 ///
74 /// # Safety
75 /// The caller must ensure that the passed-in value is a valid, open descriptor. Passing a
76 /// closed descriptor may result in dropping the object closing an unrelated opaque value which
77 /// happened to be allocated the same descriptor value. Passing an invalid descriptor value may
78 /// violate the niche requirements and result in undefined behaviour.
79 ///
80 /// The caller must ensure that only one `Descriptor` object for a given value exists at a
81 /// time, because dropping a `Descriptor` object closes the descriptor.
82 #[allow(clippy::must_use_candidate)] // This could be called and immediately dropped to close an unwanted descriptor.
83 pub const unsafe fn new(raw: u32) -> Self {
84 // SAFETY: The caller is required to pass a valid descriptor. Any valid descriptor is a
85 // small nonnegative integer. Therefore, any descriptor plus one is a small positive
86 // integer.
87 Self(NonZeroU32::new_unchecked(raw + 1))
88 }
89
90 /// Destroys a `Descriptor` object and returns the raw value.
91 ///
92 /// The caller must ensure that the descriptor is eventually closed. This function is safe
93 /// because Rust’s safety guarantees to not include reliable freeing of resources; however,
94 /// care should be taken when calling it.
95 #[must_use = "The returned descriptor will leak if not manually closed"]
96 pub const fn into_inner(self) -> u32 {
97 let ret = self.as_raw();
98 forget(self);
99 ret
100 }
101
102 /// Returns the raw descriptor value.
103 #[must_use = "This function is only useful for its return value"]
104 pub const fn as_raw(&self) -> u32 {
105 self.0.get() - 1
106 }
107
108 /// Duplicates the descriptor.
109 ///
110 /// # Errors
111 /// * [`TooManyDescriptors`](Error::TooManyDescriptors) is returned if the descriptor table is
112 /// too full and some descriptors must be closed.
113 pub fn dup(&self) -> Result<Self> {
114 // SAFETY: dup can be invoked with any valid descriptor.
115 let new_desc = Error::from_i32(unsafe { sys::dup(self.as_raw()) })?;
116 // SAFETY: dup returns a fresh, new descriptor on success.
117 Ok(unsafe { Self::new(new_desc) })
118 }
119}
120
121impl AsRaw for Owned {
122 fn as_raw(&self) -> u32 {
123 self.0.get() - 1
124 }
125}
126
127impl AsDescriptor for Owned {
128 fn as_descriptor(&self) -> Borrowed<'_> {
129 Borrowed(self.0, PhantomData)
130 }
131}
132
133impl IntoDescriptor for Owned {
134 fn into_descriptor(self) -> Owned {
135 self
136 }
137}
138
139impl Debug for Owned {
140 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
141 self.as_raw().fmt(f)
142 }
143}
144
145impl Drop for Owned {
146 fn drop(&mut self) {
147 // SAFETY: The contained descriptor is always valid. There can be only one Owned object in
148 // existence for a given open descriptor. There is no safe way to close a descriptor other
149 // than dropping the Owned object. Therefore, the descriptor is valid and closing it will
150 // not break any other objects.
151 unsafe { sys::close(self.as_raw()) };
152 }
153}
154
155impl<Context> Encode<Context> for Owned {
156 fn encode<W: Write>(
157 &self,
158 e: &mut Encoder<W>,
159 _: &mut Context,
160 ) -> core::result::Result<(), minicbor::encode::Error<W::Error>> {
161 cbor_encode(self.as_raw(), e)
162 }
163}
164
165/// A borrowed opaque value descriptor.
166///
167/// A value of this type encapsulates an opaque value descriptor. Copying or cloning it produces a
168/// new object containing the same descriptor. Dropping it does nothing. CBOR-encoding it yields an
169/// integer with the Identifier tag. While a value of this type exists, lifetime rules prevent the
170/// modification or dropping of the [`Owned`] value from which it borrowed its descriptor.
171#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
172pub struct Borrowed<'a>(NonZeroU32, PhantomData<&'a NonZeroU32>);
173
174impl Borrowed<'_> {
175 /// Returns the raw descriptor value.
176 #[must_use = "This function is only useful for its return value"]
177 pub const fn as_raw(self) -> u32 {
178 self.0.get() - 1
179 }
180}
181
182impl AsRaw for Borrowed<'_> {
183 fn as_raw(&self) -> u32 {
184 self.0.get() - 1
185 }
186}
187
188impl AsDescriptor for Borrowed<'_> {
189 fn as_descriptor(&self) -> Borrowed<'_> {
190 *self
191 }
192}
193
194impl Debug for Borrowed<'_> {
195 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
196 self.as_raw().fmt(f)
197 }
198}
199
200impl<Context> Encode<Context> for Borrowed<'_> {
201 fn encode<W: Write>(
202 &self,
203 e: &mut Encoder<W>,
204 _: &mut Context,
205 ) -> core::result::Result<(), minicbor::encode::Error<W::Error>> {
206 cbor_encode(self.as_raw(), e)
207 }
208}
209
210/// A CBOR-decoded opaque value descriptor.
211///
212/// A value of this type encapsulates an opaque value descriptor. It cannot be cloned. Dropping it
213/// does nothing; this may cause a resource leak, but resource leaks are not considered unsafe
214/// Rust, and under the circumstances, closing the descriptor could be unsafe (see the safety note
215/// on [`into_owned`](Decoded::into_owned) for why). The intended use of this type is to
216/// immediately call [`into_owned`](Decoded::into_owned) to convert the value into an [`Owned`]
217/// instead.
218#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
219pub struct Decoded(NonZeroU32);
220
221impl Decoded {
222 /// Converts a `Decoded` descriptor into an [`Owned`] descriptor.
223 ///
224 /// # Safety
225 /// The caller must ensure that the `Decoded` descriptor is the only reference to the
226 /// descriptor it holds, and that that descriptor is valid. Generally, this is accomplished by
227 /// obtaining a `Decoded` descriptor by CBOR-decoding the result of a method call, because
228 /// OC-Wasm guarantees that any opaque value returned from a method call is represented by a
229 /// fresh descriptor.
230 ///
231 /// The reason why this method is unsafe is that a caller could potentially craft an arbitrary
232 /// CBOR sequence in a byte buffer, then decode it. If such a decoding operation were to return
233 /// an [`Owned`] directly, this would be unsound, as the caller could decode a second [`Owned`]
234 /// referring to the same descriptor as an existing [`Owned`] or an [`Owned`] referring to a
235 /// closed descriptor. Instead, CBOR decoding (which is itself safe) can only create a
236 /// `Decoded`, which does not claim exclusive ownership (or even validity) of the contained
237 /// descriptor but also cannot actually be used as a descriptor; the caller is forced to
238 /// promise those properties in order to convert to the actually useful [`Owned`] type via this
239 /// `unsafe` method.
240 #[allow(clippy::must_use_candidate)] // If caller doesn’t want the descriptor, they can do this and immediately drop.
241 pub unsafe fn into_owned(self) -> Owned {
242 Owned(self.0)
243 }
244}
245
246impl Debug for Decoded {
247 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
248 (self.0.get() - 1).fmt(f)
249 }
250}
251
252impl<'b, Context> Decode<'b, Context> for Decoded {
253 fn decode(
254 d: &mut Decoder<'b>,
255 _: &mut Context,
256 ) -> core::result::Result<Self, minicbor::decode::Error> {
257 let tag = d.tag()?;
258 if tag != IDENTIFIER {
259 return Err(minicbor::decode::Error::message("expected Identifier tag"));
260 }
261 Ok(Self(NonZeroU32::new(d.u32()? + 1).unwrap()))
262 }
263}