blob: 4baf7d52cd17dbca5d11077989dd4d0c2d71a667 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use super::DispatcherOps;
use super::dispatcher::Dispatcher;
use super::process_dispatcher::ProcessDispatcher;
use core::ptr::NonNull;
use fbl::{HasRefCount, Recyclable, RefPtr};
use zx_status::Status;
use zx_types::{zx_handle_t, zx_rights_t};
/// A wrapper around a handle value received from userspace.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct HandleValue {
value: zx_handle_t,
}
impl HandleValue {
/// Constructs a new `HandleValue` from a raw handle value.
pub const fn new(value: zx_handle_t) -> Self {
Self { value }
}
/// Returns the underlying raw handle value.
pub fn raw_value(&self) -> zx_handle_t {
self.value
}
}
#[repr(transparent)]
pub struct KernelHandle<T>
where
T: HasRefCount + Recyclable + DispatcherOps,
{
ptr: *const T,
}
impl<T> KernelHandle<T>
where
T: HasRefCount + Recyclable + DispatcherOps,
{
pub fn new(dispatcher: RefPtr<T>) -> Self {
Self { ptr: RefPtr::into_raw(dispatcher) }
}
/// Casts this handle to a generic Dispatcher handle.
pub fn cast(mut self) -> KernelHandle<Dispatcher> {
let ref_ptr = self.take_ref_ptr().expect("KernelHandle was empty");
// SAFETY: Dispatcher upcasting is safe across all DispatcherOps types.
KernelHandle::new(unsafe { ref_ptr.cast() })
}
pub fn release(mut self) -> RefPtr<T> {
self.take_ref_ptr().expect("KernelHandle was empty")
}
/// Returns a reference to the underlying `RefPtr<T>`.
///
/// Callers can invoke methods directly on `T` (via `Deref`) without bumping the
/// reference count, or call `.clone()` on the returned reference to acquire a new `RefPtr<T>`.
pub fn dispatcher(&self) -> &RefPtr<T> {
assert!(!self.ptr.is_null());
// SAFETY: `self.ptr` is non-null and holds an acquired ref count. `RefPtr<T>` is `#[repr(C)]`
// containing `NonNull<T>`, which has the exact same layout and ABI as `*const T`.
unsafe { &*(core::ptr::from_ref(&self.ptr).cast::<RefPtr<T>>()) }
}
fn take_ref_ptr(&mut self) -> Option<RefPtr<T>> {
let ptr = core::mem::replace(&mut self.ptr, core::ptr::null());
if ptr.is_null() {
None
} else {
// SAFETY: `ptr` came from an active `KernelHandle` owning a reference count.
Some(unsafe { RefPtr::from_raw(ptr) })
}
}
pub fn make_and_add_handle(self, rights: zx_rights_t) -> Result<HandleValue, Status> {
ProcessDispatcher::with_current(|up| up.make_and_add_handle(self, rights))
}
}
impl<T> Drop for KernelHandle<T>
where
T: HasRefCount + Recyclable + DispatcherOps,
{
fn drop(&mut self) {
if let Some(ref_ptr) = self.take_ref_ptr() {
ref_ptr.on_zero_handles();
}
}
}
/// Safe RAII wrapper for an owned handle (`HandleOwner`).
pub struct HandleOwner {
ptr: NonNull<core::ffi::c_void>,
}
impl HandleOwner {
/// Creates a new `HandleOwner` from a non-null raw handle pointer.
///
/// # Safety
/// `ptr` must be a valid, owned `Handle*` created by C++ `Handle::Make` or `Handle::Dup`.
pub unsafe fn from_raw(ptr: *mut core::ffi::c_void) -> Option<Self> {
NonNull::new(ptr).map(|ptr| Self { ptr })
}
/// Releases the raw pointer from `HandleOwner` without running its destructor.
pub fn release(self) -> *mut core::ffi::c_void {
let ptr = self.ptr.as_ptr();
core::mem::forget(self);
ptr
}
/// Duplicates this handle with the given rights.
pub fn dup(&self, rights: zx_rights_t) -> Result<Self, Status> {
// SAFETY: `self.ptr` is guaranteed to be a valid non-null handle pointer.
let raw = unsafe { cpp_handle_dup(self.ptr.as_ptr(), rights) };
// SAFETY: `cpp_handle_dup` returns a valid new raw handle or null.
unsafe { Self::from_raw(raw).ok_or(Status::NO_MEMORY) }
}
/// Returns the raw pointer backing this handle owner.
pub fn as_raw(&self) -> *mut core::ffi::c_void {
self.ptr.as_ptr()
}
}
impl Drop for HandleOwner {
fn drop(&mut self) {
// SAFETY: `self.ptr` is valid and owned.
unsafe { cpp_handle_destroy(self.ptr.as_ptr()) };
}
}
unsafe extern "C" {
fn cpp_handle_dup(
handle: *const core::ffi::c_void,
rights: zx_rights_t,
) -> *mut core::ffi::c_void;
fn cpp_handle_destroy(handle: *mut core::ffi::c_void);
}