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}