mundane/boringssl/wrapper.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
5use std::marker::PhantomData;
6use std::mem::{self, MaybeUninit};
7use std::ptr::NonNull;
8
9/// A trait that can be used to ensure that users of the boringssl module can't
10/// implement a trait.
11///
12/// See the [API Guidelines] for details.
13///
14/// [API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
15pub trait Sealed {}
16
17macro_rules! sealed {
18 ($name:ident) => {
19 impl ::boringssl::wrapper::Sealed for ::boringssl::raw::ffi::$name {}
20 };
21}
22
23macro_rules! impl_traits {
24 (@inner $name:ident, CNew => $fn:tt) => {
25 c_new!($name, $fn);
26 };
27 (@inner $name:ident, CUpRef => $fn:tt) => {
28 c_up_ref!($name, $fn);
29 };
30 (@inner $name:ident, CFree => $fn:tt) => {
31 c_free!($name, $fn);
32 };
33 (@inner $name:ident, CInit => $fn:tt) => {
34 c_init!($name, $fn);
35 };
36 (@inner $name:ident, CDestruct => $fn:tt) => {
37 c_destruct!($name, $fn);
38 };
39 (@inner $name:ident, $trait:ident => $fn:tt) => {
40 compile_error!(concat!("unrecognized trait ", stringify!($trait)));
41 };
42 ($name:ident, $($trait:ident => $fn:tt),*) => {
43 sealed!($name);
44 $(impl_traits!(@inner $name, $trait => $fn);)*
45 };
46}
47
48#[allow(clippy::missing_safety_doc)] // TODO(https://fxbug.dev/42181467)
49/// A C object from the BoringSSL API which can be allocated and constructed.
50pub unsafe trait CNew: Sealed {
51 /// Returns a new, constructed, heap-allocated object, or NULL on failure.
52 ///
53 /// This should not be called directly; instead, use `new`.
54 #[deprecated(note = "do not call new_raw directly; instead, call new")]
55 unsafe fn new_raw() -> *mut Self;
56
57 /// Returns a new, constructed, heap-allocated object, or `None` on failure.
58 #[must_use]
59 unsafe fn new() -> Option<NonNull<Self>> {
60 #[allow(deprecated)]
61 NonNull::new(Self::new_raw())
62 }
63}
64
65macro_rules! c_new {
66 ($name:ident, $new:ident) => {
67 unsafe impl ::boringssl::wrapper::CNew for ::boringssl::raw::ffi::$name {
68 unsafe fn new_raw() -> *mut Self {
69 ::boringssl::raw::ffi::$new()
70 }
71 }
72 };
73}
74
75#[allow(clippy::missing_safety_doc)] // TODO(https://fxbug.dev/42181466)
76/// A C object from the BoringSSL API which has a reference count that can be
77/// increased.
78pub unsafe trait CUpRef: Sealed {
79 /// Increases an object's reference count.
80 unsafe fn up_ref(slf: *mut Self);
81}
82
83macro_rules! c_up_ref {
84 ($name:ident, $up_ref:ident) => {
85 unsafe impl ::boringssl::wrapper::CUpRef for ::boringssl::raw::ffi::$name {
86 unsafe fn up_ref(slf: *mut Self) {
87 use boringssl::abort::UnwrapAbort;
88 ::boringssl::raw::one_or_err(
89 stringify!($up_ref),
90 ::boringssl::raw::ffi::$up_ref(slf),
91 )
92 .unwrap_abort()
93 }
94 }
95 };
96}
97
98#[allow(clippy::missing_safety_doc)] // TODO(https://fxbug.dev/42181466)
99/// A C object from the BoringSSL API which can be freed.
100pub unsafe trait CFree: Sealed {
101 /// Frees a heap-allocated object.
102 ///
103 /// If this is a reference-counted object, `free` decrements the reference
104 /// count, and frees the object if it reaches zero. Otherwise, if this is
105 /// not a reference-counted object, it frees it.
106 unsafe fn free(slf: *mut Self);
107}
108
109macro_rules! c_free {
110 ($name:ident, $free:ident) => {
111 unsafe impl ::boringssl::wrapper::CFree for ::boringssl::raw::ffi::$name {
112 unsafe fn free(slf: *mut Self) {
113 ::boringssl::raw::ffi::$free(slf)
114 }
115 }
116 };
117}
118
119#[allow(clippy::missing_safety_doc)] // TODO(https://fxbug.dev/42181466)
120/// A C object from the BoringSSL API which can be initialized.
121pub unsafe trait CInit: Sealed {
122 /// Initializes an uninitialized object.
123 ///
124 /// # Safety
125 ///
126 /// `init` must not be called on an initialized object.
127 unsafe fn init(slf: *mut Self);
128}
129
130#[allow(unused)] // TODO: Remove once it's used in the 'raw' module
131macro_rules! c_init {
132 ($name:ident, $init:ident) => {
133 unsafe impl ::boringssl::wrapper::CInit for ::boringssl::raw::ffi::$name {
134 unsafe fn init(slf: *mut Self) {
135 ::boringssl::raw::ffi::$init(slf)
136 }
137 }
138 };
139}
140
141#[allow(clippy::missing_safety_doc)] // TODO(https://fxbug.dev/42181466)
142/// A C object from the BoringSSL API which can be destructed.
143pub unsafe trait CDestruct: Sealed {
144 /// Destructs an initialized object.
145 ///
146 /// # Safety
147 ///
148 /// `slf` must be an initialized object. After a call to `destruct`, `slf`
149 /// is uninitialized.
150 unsafe fn destruct(slf: *mut Self);
151}
152
153macro_rules! c_destruct {
154 ($name:ident, _) => {
155 unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
156 unsafe fn destruct(_slf: *mut Self) {}
157 }
158 };
159 ($name:ident, $destruct:tt) => {
160 unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
161 unsafe fn destruct(slf: *mut Self) {
162 ::boringssl::raw::ffi::$destruct(slf)
163 }
164 }
165 };
166}
167
168/// A wrapper around a pointer to a heap-allocated, constructed C object from
169/// the BoringSSL API.
170///
171/// `CHeapWrapper` maintains the invariant that the object it references is
172/// always allocated and constructed. This means that:
173/// - If the object can be reference counted, `CHeapWrapper` implements `Clone`
174/// by incrementing the reference count, and decrementing on `Drop`.
175/// - If the object cannot be reference counted, `CHeapWrapper` does not
176/// implement `Clone`, but will still free the object on `Drop`.
177///
178/// `CHeapWrapper`s are not thread-safe; they do not implement `Send` or `Sync`.
179pub struct CHeapWrapper<C: CFree> {
180 // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
181 // is changed, make sure it's still !Send + !Sync.
182 obj: NonNull<C>,
183}
184
185impl<C: CFree> CHeapWrapper<C> {
186 /// Takes ownership of a constructed object.
187 ///
188 /// # Safety
189 ///
190 /// `obj` must point to an allocated, constructed object. The caller must
191 /// ensure that, when the returned `CHeapWrapper` is dropped, it is safe to
192 /// call `C::free` on `obj`. In most cases, this means that the caller
193 /// should not free `obj`, and instead consider ownership of `obj` to have
194 /// transferred to the new `CHeapWrapper`.
195 ///
196 /// The caller must also ensure that no pointers to the object will ever be
197 /// used by other threads so long as this `CHeapWrapper` exists.
198 #[must_use]
199 pub unsafe fn new_from(obj: NonNull<C>) -> CHeapWrapper<C> {
200 CHeapWrapper { obj }
201 }
202
203 #[must_use]
204 pub fn as_mut(&mut self) -> *mut C {
205 self.obj.as_ptr()
206 }
207
208 #[must_use]
209 pub fn as_const(&self) -> *const C {
210 self.obj.as_ptr()
211 }
212
213 /// Consumes this `CHeapWrapper` and return the underlying pointer.
214 ///
215 /// The object will not be freed. Instead, the caller takes logical
216 /// ownership of the object.
217 #[must_use]
218 pub fn into_mut(self) -> *mut C {
219 // NOTE: This method safe for the same reason that mem::forget is safe:
220 // it's equivalent to sending it to a thread that goes to sleep forever
221 // or creating a Rc cycle or some other silly-but-safe behavior.
222 let ptr = self.obj.as_ptr();
223 mem::forget(self);
224 ptr
225 }
226}
227
228impl<C: CNew + CFree> Default for CHeapWrapper<C> {
229 fn default() -> CHeapWrapper<C> {
230 // TODO(joshlf): In order for this to be safe, CNew must provide the
231 // safety guarantee that it's always safe to call CNew::new and then
232 // later to call CFree::free on that object (e.g., see the safety
233 // comment on CStackWrapper::new).
234 unsafe {
235 use boringssl::abort::UnwrapAbort;
236 let obj = C::new().expect_abort("could not allocate object");
237 CHeapWrapper { obj }
238 }
239 }
240}
241
242impl<C: CUpRef + CFree> Clone for CHeapWrapper<C> {
243 fn clone(&self) -> CHeapWrapper<C> {
244 unsafe { C::up_ref(self.obj.as_ptr()) };
245 CHeapWrapper { obj: self.obj }
246 }
247}
248
249impl<C: CFree> Drop for CHeapWrapper<C> {
250 fn drop(&mut self) {
251 unsafe { C::free(self.obj.as_ptr()) };
252 }
253}
254
255/// A wrapper around a pointer to a C object from the BoringSSL API.
256///
257/// Unlike `CHeapWrapper` or `CStackWrapper`, `CRef` does not own the pointed-to
258/// object, but merely borrows it like a normal Rust reference. The only reason
259/// to use `CRef<C>` instead of a `&C` is to make it so that access to the `C`
260/// is unsafe, as `CRef` only exposes a raw pointer accessor for its object.
261///
262/// `CRef` maintains the invariant that the object it references is always
263/// allocated and constructed, and that mutable access to the object is disabled
264/// for the lifetime of the `CRef`.
265pub struct CRef<'a, C> {
266 // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
267 // is changed, make sure it's still !Send + !Sync.
268 obj: NonNull<C>,
269 // Make sure CRef has the lifetime 'a.
270 _lifetime: PhantomData<&'a ()>,
271}
272
273impl<'a, C> CRef<'a, C> {
274 /// Creates a new `CRef` from a raw pointer.
275 ///
276 /// # Safety
277 ///
278 /// `obj` must point to an allocated, constructed object. The caller must
279 /// ensure that, for the lifetime, `'a`, `obj` will continue to point to the
280 /// same allocated, constructed object, and that mutable access to the
281 /// object will be disallowed.
282 ///
283 /// The caller must also ensure that no other pointers to the object will
284 /// ever be sent to other threads so long as this `CRef` exists.
285 #[must_use]
286 pub unsafe fn new(obj: NonNull<C>) -> CRef<'a, C> {
287 CRef { obj, _lifetime: PhantomData }
288 }
289
290 #[must_use]
291 pub fn as_const(&self) -> *const C {
292 self.obj.as_ptr()
293 }
294}
295
296/// A wrapper around a constructed C object from the BoringSSL API.
297///
298/// `CStackWrapper` maintains the invariant that the object it contains is
299/// always constructed. The object is destructed on `Drop`.
300///
301/// `CStackWrapper`s are not thread-safe; they do not implement `Send` or
302/// `Sync`.
303pub struct CStackWrapper<C: CDestruct> {
304 obj: C,
305 // Make sure CStackWrapper doesn't implement Send or Sync regardless of C.
306 _no_sync: PhantomData<*mut ()>,
307}
308
309impl<C: CDestruct> CStackWrapper<C> {
310 /// Constructs a new `CStackWrapper`.
311 ///
312 /// # Safety
313 ///
314 /// `obj` must be constructed, and it must be safe for `C::destruct` to be
315 /// called on `obj` when this `CStackWrapper` is dropped.
316 #[must_use]
317 pub unsafe fn new(obj: C) -> CStackWrapper<C> {
318 CStackWrapper { obj, _no_sync: PhantomData }
319 }
320
321 #[must_use]
322 pub fn as_c_ref(&mut self) -> CRef<'_, C> {
323 unsafe { CRef::new(NonNull::new_unchecked(&mut self.obj as *mut C)) }
324 }
325
326 #[must_use]
327 pub fn as_mut(&mut self) -> *mut C {
328 &mut self.obj
329 }
330
331 #[must_use]
332 pub fn as_const(&self) -> *const C {
333 &self.obj
334 }
335}
336
337impl<C: CInit + CDestruct> Default for CStackWrapper<C> {
338 // TODO(joshlf): In order for this to be safe, CInit must provide the safety
339 // guarantee that it's always safe to call CInit::init and then later to
340 // call CDestruct::destruct on that object (e.g., see the safety comment on
341 // CStackWrapper::new).
342 fn default() -> CStackWrapper<C> {
343 unsafe {
344 let mut obj = MaybeUninit::<C>::uninit();
345 C::init(obj.as_mut_ptr());
346 CStackWrapper { obj: obj.assume_init(), _no_sync: PhantomData }
347 }
348 }
349}
350
351impl<C: CDestruct> Drop for CStackWrapper<C> {
352 fn drop(&mut self) {
353 unsafe { C::destruct(&mut self.obj) }
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use boringssl::EC_KEY;
361
362 #[test]
363 fn test_heap_wrapper_into_mut() {
364 // Test that CHeapWrapper::into_mut doesn't free the pointer. If it
365 // does, then EC_KEY::free is likely (though not guaranteed) to abort
366 // when it finds the refcount at 0.
367 let key = CHeapWrapper::<EC_KEY>::default();
368 unsafe { EC_KEY::free(key.into_mut()) };
369 }
370}