| // Copyright 2026 The BoringSSL Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use core::{ |
| marker::PhantomData, |
| ptr::{NonNull, null}, |
| slice::from_raw_parts, |
| }; |
| |
| use bssl_crypto::FfiSlice; |
| |
| use crate::errors::{PkiError, X509Error}; |
| |
| /// This method catches panics before they cross language boundary. |
| #[must_use] |
| #[inline] |
| pub(crate) fn maybe_panic<T>(work: impl FnOnce() -> T) -> T { |
| let assert_unwind_safe = core::panic::AssertUnwindSafe(work); |
| let call = move || { |
| let core::panic::AssertUnwindSafe(work) = { assert_unwind_safe }; |
| work() |
| }; |
| #[cfg(feature = "std")] |
| let res = match std::panic::catch_unwind(call) { |
| Ok(res) => res, |
| Err(e) => { |
| eprintln!("panic about to cross FFI: {:?}", e); |
| std::process::abort() |
| } |
| }; |
| #[cfg(not(feature = "std"))] |
| let res = call(); |
| res |
| } |
| |
| pub(crate) fn slice_into_ffi_raw_parts<T>(slice: &[T]) -> (*const T, usize) { |
| if slice.is_empty() { |
| (null(), 0) |
| } else { |
| (slice.as_ptr(), slice.len()) |
| } |
| } |
| |
| /// Sanitize the data pointer and length and reconstitute the slice. |
| /// |
| /// This method returns an empty slice if the length is 0 or the pointer is NULL. |
| /// # Safety |
| /// Caller must ensure that `'a` outlives `input`. |
| #[inline] |
| pub(crate) unsafe fn sanitize_slice<'a, T>(input: *const T, len: usize) -> Option<&'a [T]> { |
| if len == 0 || input.is_null() { |
| return Some(&[]); |
| } |
| if !input.is_aligned() || len.checked_mul(size_of::<T>())? > isize::MAX as usize { |
| return None; |
| } |
| unsafe { |
| // Safety: the pointer and the size has been sanitised. |
| Some(from_raw_parts(input, len)) |
| } |
| } |
| |
| /// BIO wrapper only for internal use. |
| pub(crate) struct Bio<'a>(NonNull<bssl_sys::BIO>, PhantomData<&'a ()>); |
| |
| impl<'a> Bio<'a> { |
| /// # Safety |
| /// Caller must ensure that the lifetime of this BIO outlives the backing object. |
| /// It is strongly recommended to call the builder functions. |
| pub(crate) unsafe fn new(bio: NonNull<bssl_sys::BIO>) -> Self { |
| Bio(bio, PhantomData) |
| } |
| |
| pub fn from_bytes(buf: &'a [u8]) -> Result<Self, PkiError> { |
| let len = if let Ok(len) = buf.len().try_into() { |
| len |
| } else { |
| return Err(PkiError::X509(X509Error::PemTooLong)); |
| }; |
| let mem_buf = unsafe { |
| // Safety: buf is still valid |
| bssl_sys::BIO_new_mem_buf(buf.as_ffi_void_ptr(), len) |
| }; |
| let Some(mem_buf) = NonNull::new(mem_buf) else { |
| panic!("bio: allocation failure"); |
| }; |
| Ok(unsafe { |
| // Safety: our returned object is outlived by the input buffer. |
| Self::new(mem_buf) |
| }) |
| } |
| |
| pub fn ptr(&mut self) -> *mut bssl_sys::BIO { |
| self.0.as_ptr() |
| } |
| } |
| |
| impl<'a> Drop for Bio<'a> { |
| fn drop(&mut self) { |
| unsafe { |
| // Safety: the BIO handle should still be valid |
| bssl_sys::BIO_free(self.0.as_ptr()); |
| } |
| } |
| } |