blob: 2cd03f15de0ecc03a9bf337baaf03af89828174b [file] [edit]
// 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,
mem::MaybeUninit,
ptr::{
NonNull,
null,
null_mut, //
},
slice::{
from_raw_parts,
from_raw_parts_mut, //
}, //
};
use bssl_crypto::FfiSlice;
use crate::{
context::CertificateCache,
errors::{
Error,
IoError, //
}, //
};
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())
}
}
pub(crate) fn mut_slice_into_ffi_raw_parts<T>(slice: &mut [T]) -> (*mut T, usize) {
if slice.is_empty() {
(null_mut(), 0)
} else {
(slice.as_mut_ptr(), slice.len())
}
}
/// Safety: use it to wrap allocation only if the pointer is
/// to be freed with `OPENSSL_free`.
pub(crate) struct Alloc<T>(pub *mut T);
impl<T> Drop for Alloc<T> {
fn drop(&mut self) {
unsafe {
// Safety: `self.0` is still valid at dropping, even if it is `NULL`.
bssl_sys::OPENSSL_free(self.0 as _);
}
}
}
/// 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))
}
}
/// Sanitize the data pointer and length and reconstitute the mutable slice.
///
/// `capacity` counts the number of `T`s that `out` can hold, **not number of bytes**.
///
/// 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 sanitise_mut_byteslice<'a>(
out: *mut u8,
capacity: usize,
) -> Option<&'a mut [u8]> {
if capacity == 0 || out.is_null() {
return Some(&mut []);
}
if capacity > isize::MAX as usize {
return None;
}
unsafe {
// Safety: `out` is 1-aligned and `0` is a valid pattern for `u8`.
core::ptr::write_bytes(out, 0, capacity);
Some(from_raw_parts_mut(out, capacity))
}
}
pub(crate) fn crypto_buffer_from_buf(
buf: &[u8],
pool: Option<&CertificateCache>,
) -> Result<NonNull<bssl_sys::CRYPTO_BUFFER>, Error> {
let pool = if let Some(pool) = pool {
pool.ptr()
} else {
null_mut()
};
let (ptr, len) = slice_into_ffi_raw_parts(buf);
let buf = unsafe {
// Safety: `ptr` and `len` are valid and sanitised.
bssl_sys::CRYPTO_BUFFER_new(ptr, len, pool)
};
let buf = NonNull::new(buf).expect("allocation failure");
Ok(buf)
}
/// 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, Error> {
let len = if let Ok(len) = buf.len().try_into() {
len
} else {
return Err(Error::Io(IoError::TooLong));
};
let mem_buf = unsafe {
// Safety: buf is still valid
bssl_sys::BIO_new_mem_buf(buf.as_ffi_void_ptr(), len)
};
let mem_buf = NonNull::new(mem_buf).expect("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());
}
}
}
/// A buffer region that can be safely written to.
pub struct ReceiveBuffer<'a> {
ptr: *mut u8,
capacity: usize,
cursor: usize,
_p: PhantomData<&'a mut [u8]>,
}
impl<'a> ReceiveBuffer<'a> {
/// Create a new receiver buffer, with uninitialised bytes.
pub fn new_uninit(buffer: &'a mut [MaybeUninit<u8>]) -> Self {
let (ptr, capacity) = mut_slice_into_ffi_raw_parts(buffer);
ReceiveBuffer {
ptr: ptr as _,
capacity,
cursor: 0,
_p: PhantomData,
}
}
/// Create a new receiver buffer.
pub fn new(buffer: &'a mut [u8]) -> Self {
let (ptr, capacity) = mut_slice_into_ffi_raw_parts(buffer);
ReceiveBuffer {
ptr,
capacity,
cursor: 0,
_p: PhantomData,
}
}
/// Return a pointer to the first byte of the unfilled bytes.
///
/// **INTERNAL function only, keep it as pub(crate)**
///
/// # Example for BoringSSL Authors
///
/// ```ignore
/// let mut recv_buf: ReceiveBuffer<'_>;
/// let nr_recv = unsafe {
/// // Safety: ...
/// bssl_sys::SSL_read(ssl, recv_buf.head(), recv_buf.remaining())
/// };
/// assert!(nr_recv >= 0);
/// unsafe {
/// // Safety: by BoringSSL contract, it is guaranteed that nr_recv bytes are filled.
/// recv_buf.advance(nr_recv as usize);
/// }
/// ```
///
/// # Safety
/// - all uses of the returned buffer must be outlived by `'a`.
/// - all reads into the byte under the returned pointer and those [`Self::remaining`]
/// bytes following it must be proceeded by at least one write; otherwise, it is **undefined
/// behaviour**.
pub(crate) unsafe fn head(&mut self) -> *mut u8 {
debug_assert!(self.cursor <= self.capacity && self.cursor <= isize::MAX as usize);
unsafe {
// Safety: the cursor is still in-bound and the buffer is still owned by `self`.
self.ptr.add(self.cursor)
}
}
/// Advance the cursor to mark `bytes` from the [`Self::head`] as filled.
///
/// **INTERNAL function only, keep it as pub(crate)**
///
/// # Safety
/// The bytes in between [`Self::head`] and `Self::head() + bytes` must have been filled
/// by the caller before calling this method.
pub(crate) unsafe fn advance(&mut self, bytes: usize) {
self.cursor += bytes;
debug_assert!(self.cursor <= self.capacity);
}
/// Extract a slice to the filled data.
pub fn filled(&self) -> &[u8] {
unsafe {
// Safety: `self` still exclusively owns the buffer region and the range of bytes
// is known to be initialised by us. See `advance`.
sanitize_slice(self.ptr, self.cursor).unwrap_or(&[])
}
}
/// Reports remaining capacity in the destination buffer.
pub fn remaining(&self) -> usize {
self.capacity - self.cursor
}
/// Reports written data in the destination buffer.
pub fn written(&self) -> usize {
self.cursor
}
}
impl core::ops::Deref for ReceiveBuffer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.filled()
}
}