diff options
Diffstat (limited to 'third_party/rust/nss-gk-api/src/util.rs')
-rw-r--r-- | third_party/rust/nss-gk-api/src/util.rs | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/third_party/rust/nss-gk-api/src/util.rs b/third_party/rust/nss-gk-api/src/util.rs new file mode 100644 index 0000000000..f06542eb45 --- /dev/null +++ b/third_party/rust/nss-gk-api/src/util.rs @@ -0,0 +1,246 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::convert::TryFrom; +use std::marker::PhantomData; +use std::mem; +use std::os::raw::c_uint; +use std::ptr::null_mut; + +use crate::prtypes::*; +use crate::nss_prelude::*; + +/// Implement a smart pointer for NSS objects. +/// +/// Most of the time the pointer is like a `Box`, but there are exceptions (e.g. +/// PK11SymKey is internally reference counted so its pointer is like an `Arc`.) +/// +/// Named "scoped" because that is what NSS calls its `unique_ptr` typedefs. +macro_rules! scoped_ptr { + ($name:ident, $target:ty, $dtor:path) => { + pub struct $name { + ptr: *mut $target, + } + + impl $name { + /// Create a new instance of `$name` from a pointer. + /// + /// # Errors + /// When passed a null pointer generates an error. + pub unsafe fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> { + let ptr = $crate::err::into_result(raw)?; + Ok(Self { ptr }) + } + } + + impl $crate::err::IntoResult for *mut $target { + type Ok = $name; + + unsafe fn into_result(self) -> Result<Self::Ok, $crate::err::Error> { + $name::from_ptr(self) + } + } + + impl std::ops::Deref for $name { + type Target = *mut $target; + #[must_use] + fn deref(&self) -> &*mut $target { + &self.ptr + } + } + + // Original implements DerefMut, but is that really a good idea? + + impl Drop for $name { + fn drop(&mut self) { + unsafe { $dtor(self.ptr) }; + } + } + } +} + +macro_rules! impl_clone { + ($name:ty, $nss_fn:path) => { + impl Clone for $name { + #[must_use] + fn clone(&self) -> Self { + let ptr = unsafe { $nss_fn(self.ptr) }; + assert!(!ptr.is_null()); + Self { ptr } + } + } + } +} + +impl SECItem { + /// Return contents as a slice. + /// + /// Unsafe due to calling from_raw_parts, or if 'a outlives &self. This + /// unsafety is encapsulated by the `as_slice` method of `SECItemBorrowed` + /// and `SECItemMut`. + /// + /// Note that safe code can construct a SECItem pointing to anything. The + /// same is not true of the safe wrappers `SECItemMut` and `SECItemBorrowed` + /// because their inner SECItem is private. + pub unsafe fn as_slice<'a>(&self) -> &'a [u8] { + // Sanity check the type, as some types don't count bytes in `Item::len`. + assert_eq!(self.type_, SECItemType::siBuffer); + // Note: `from_raw_parts` requires non-null `data` even for zero-length + // slices. + if self.len != 0 { + std::slice::from_raw_parts(self.data, usize::try_from(self.len).unwrap()) + } else { + &[] + } + } +} + +/// An owned SECItem. +/// +/// The SECItem structure is allocated by Rust. The buffer referenced by the +/// SECItem is allocated by NSS. `SECITEM_FreeItem` will be called to free the +/// buffer when the SECItemMut is dropped. +/// +/// This is used with NSS functions that return a variable amount of data. +#[repr(transparent)] +pub struct SECItemMut { + inner: SECItem, +} + +impl<'a> Drop for SECItemMut { + #[allow(unused_must_use)] + fn drop(&mut self) { + // FreeItem unconditionally frees the buffer referenced by the SECItem. + // If the second argument is true, it also frees the SECItem itself, + // which we don't want to do, because rust owns that memory. + unsafe { SECITEM_FreeItem(&mut self.inner, PRBool::from(false)) }; + } +} + +impl AsRef<SECItem> for SECItemMut { + fn as_ref(&self) -> &SECItem { + &self.inner + } +} + +impl AsMut<SECItem> for SECItemMut { + fn as_mut(&mut self) -> &mut SECItem { + &mut self.inner + } +} + +impl SECItemMut { + /// Return contents as a slice. + pub fn as_slice(&self) -> &[u8] { + unsafe { self.inner.as_slice() } + } + + /// Make an empty `SECItemMut` for passing as a mutable `*SECItem` argument. + pub fn make_empty() -> SECItemMut { + SECItemMut { + inner: SECItem { + type_: SECItemType::siBuffer, + data: null_mut(), + len: 0, + } + } + } +} + +/// A borrowed SECItem. +/// +/// The SECItem structure is allocated by Rust. The buffer referenced by the +/// SECItem may be allocated either by Rust or NSS. The SECItem does not own the +/// buffer and will not free it when dropped. +/// +/// This is usually used to pass a reference to some borrowed rust memory to +/// NSS. It is occasionally used to accept non-owned output data from NSS. +#[repr(transparent)] +pub struct SECItemBorrowed<'a> { + inner: SECItem, + phantom_data: PhantomData<&'a u8>, +} + +impl<'a> AsRef<SECItem> for SECItemBorrowed<'a> { + fn as_ref(&self) -> &SECItem { + &self.inner + } +} + +impl<'a> AsMut<SECItem> for SECItemBorrowed<'a> { + /// Get a mutable reference to the underlying SECItem struct. + /// + /// Note that even if the SECItem struct is mutable, the buffer it + /// references may not be. Take care not to pass the mutable + /// SECItem to NSS routines that will violate mutability rules. + // + // TODO: Should we make the danger more obvious, by using a non-trait method + // with "unsafe" in the name, or an unsafe method? + fn as_mut(&mut self) -> &mut SECItem { + &mut self.inner + } +} + +impl<'a> SECItemBorrowed<'a> { + /// Return contents as a slice. + pub fn as_slice(&self) -> &'a [u8] { + unsafe { self.inner.as_slice() } + } + + /// Create an empty `SECItemBorrowed`. + /// + /// This can be used (1) to pass an empty item as an argument, and (2) as an + /// output parameter when NSS returns a pointer to NSS-owned memory that + /// should not be freed when the SECItem is dropped. If the memory should + /// be freed when the SECItem is dropped, use SECItemMut. + /// + /// It is safe to let the caller specify any lifetime here because no + /// borrowing is actually taking place. However, if the pointer in the + /// returned item is modified, care must be taken that the specified + /// lifetime accurately reflects the data referenced by the pointer. + pub fn make_empty() -> SECItemBorrowed<'a> { + SECItemBorrowed { + inner: SECItem { + type_: SECItemType::siBuffer, + data: null_mut(), + len: 0, + }, + phantom_data: PhantomData, + } + } + + /// Create a `SECItemBorrowed` wrapping a slice. + /// + /// Creating this object is technically safe, but using it is extremely dangerous. + /// Minimally, it can only be passed as a `const SECItem*` argument to functions, + /// or those that treat their argument as `const`. + pub fn wrap(buf: &'a [u8]) -> SECItemBorrowed<'a> { + SECItemBorrowed { + inner: SECItem { + type_: SECItemType::siBuffer, + data: buf.as_ptr() as *mut u8, + len: c_uint::try_from(buf.len()).unwrap(), + }, + phantom_data: PhantomData, + } + } + + /// Create a `SECItemBorrowed` wrapping a struct. + /// + /// Creating this object is technically safe, but using it is extremely dangerous. + /// Minimally, it can only be passed as a `const SECItem*` argument to functions, + /// or those that treat their argument as `const`. + pub fn wrap_struct<T>(v: &'a T) -> SECItemBorrowed<'a> { + SECItemBorrowed { + inner: SECItem { + type_: SECItemType::siBuffer, + data: (v as *const T as *mut T).cast(), + len: c_uint::try_from(mem::size_of::<T>()).unwrap(), + }, + phantom_data: PhantomData, + } + } +} |