// Copyright (C) 2024 The Android Open Source Project // // 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 // // http://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 android_hardware_common::{ aidl::android::hardware::common::NativeHandle::NativeHandle as AidlNativeHandle, binder::ParcelFileDescriptor, }; use std::{ ffi::c_int, mem::forget, os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, ptr::NonNull, }; /// Rust wrapper around `native_handle_t`. /// /// This owns the `native_handle_t` and its file descriptors, and will close them and free it when /// it is dropped. #[derive(Debug)] pub struct NativeHandle(NonNull); impl NativeHandle { /// Creates a new `NativeHandle` with the given file descriptors and integer values. /// /// The `NativeHandle` will take ownership of the file descriptors and close them when it is /// dropped. pub fn new(fds: Vec, ints: &[c_int]) -> Option { let fd_count = fds.len(); // SAFETY: native_handle_create doesn't have any safety requirements. let handle = unsafe { ffi::native_handle_create(fd_count.try_into().unwrap(), ints.len().try_into().unwrap()) }; let handle = NonNull::new(handle)?; for (i, fd) in fds.into_iter().enumerate() { // SAFETY: `handle` must be valid because it was just created, and the array offset is // within the bounds of what we allocated above. unsafe { *(*handle.as_ptr()).data.as_mut_ptr().add(i) = fd.into_raw_fd(); } } for (i, value) in ints.iter().enumerate() { // SAFETY: `handle` must be valid because it was just created, and the array offset is // within the bounds of what we allocated above. Note that `data` is uninitialized // until after this so we can't use `slice::from_raw_parts_mut` or similar to create a // reference to it so we use raw pointers arithmetic instead. unsafe { *(*handle.as_ptr()).data.as_mut_ptr().add(fd_count + i) = *value; } } // SAFETY: `handle` must be valid because it was just created. unsafe { ffi::native_handle_set_fdsan_tag(handle.as_ptr()); } Some(Self(handle)) } /// Returns a borrowed view of all the file descriptors in this native handle. pub fn fds(&self) -> Vec { self.data()[..self.fd_count()] .iter() .map(|fd| { // SAFETY: The `native_handle_t` maintains ownership of the file descriptor so it // won't be closed until this `NativeHandle` is destroyed. The `BorrowedFd` will // have a lifetime constrained to that of `&self`, so it can't outlive it. unsafe { BorrowedFd::borrow_raw(*fd) } }) .collect() } /// Returns the integer values in this native handle. pub fn ints(&self) -> &[c_int] { &self.data()[self.fd_count()..] } /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained. pub fn into_fds(self) -> Vec { // Unset FDSan tag since this `native_handle_t` is no longer the owner of the file // descriptors after this function. // SAFETY: Our wrapped `native_handle_t` pointer is always valid. unsafe { ffi::native_handle_unset_fdsan_tag(self.as_ref()); } let fds = self.data()[..self.fd_count()] .iter() .map(|fd| { // SAFETY: The `native_handle_t` has ownership of the file descriptor, and // after this we destroy it without closing the file descriptor so we can take over // ownership of it. unsafe { OwnedFd::from_raw_fd(*fd) } }) .collect(); // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed // after this because we own it and forget it. unsafe { assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); } // Don't drop self, as that would cause `native_handle_close` to be called and close the // file descriptors. forget(self); fds } /// Returns a reference to the underlying `native_handle_t`. fn as_ref(&self) -> &ffi::native_handle_t { // SAFETY: All the ways of creating a `NativeHandle` ensure that the `native_handle_t` is // valid and initialised, and lives as long as the `NativeHandle`. We enforce Rust's // aliasing rules by giving the reference a lifetime matching that of `&self`. unsafe { self.0.as_ref() } } /// Returns the number of file descriptors included in the native handle. fn fd_count(&self) -> usize { self.as_ref().numFds.try_into().unwrap() } /// Returns the number of integer values included in the native handle. fn int_count(&self) -> usize { self.as_ref().numInts.try_into().unwrap() } /// Returns a slice reference for all the used `data` field of the native handle, including both /// file descriptors and integers. fn data(&self) -> &[c_int] { let total_count = self.fd_count() + self.int_count(); // SAFETY: The data must have been initialised with this number of elements when the // `NativeHandle` was created. unsafe { self.as_ref().data.as_slice(total_count) } } /// Wraps a raw `native_handle_t` pointer, taking ownership of it. /// /// # Safety /// /// `native_handle` must be a valid pointer to a `native_handle_t`, and must not be used /// anywhere else after calling this method. pub unsafe fn from_raw(native_handle: NonNull) -> Self { Self(native_handle) } /// Creates a new `NativeHandle` wrapping a clone of the given `native_handle_t` pointer. /// /// Unlike [`from_raw`](Self::from_raw) this doesn't take ownership of the pointer passed in, so /// the caller remains responsible for closing and freeing it. /// /// # Safety /// /// `native_handle` must be a valid pointer to a `native_handle_t`. pub unsafe fn clone_from_raw(native_handle: NonNull) -> Option { // SAFETY: The caller promised that `native_handle` was valid. let cloned = unsafe { ffi::native_handle_clone(native_handle.as_ptr()) }; NonNull::new(cloned).map(Self) } /// Returns a raw pointer to the wrapped `native_handle_t`. /// /// This is only valid as long as this `NativeHandle` exists, so shouldn't be stored. It mustn't /// be closed or deleted. pub fn as_raw(&self) -> NonNull { self.0 } /// Turns the `NativeHandle` into a raw `native_handle_t`. /// /// The caller takes ownership of the `native_handle_t` and its file descriptors, so is /// responsible for closing and freeing it. pub fn into_raw(self) -> NonNull { let raw = self.0; forget(self); raw } } impl Clone for NativeHandle { fn clone(&self) -> Self { // SAFETY: Our wrapped `native_handle_t` pointer is always valid. unsafe { Self::clone_from_raw(self.0) }.expect("native_handle_clone returned null") } } impl Drop for NativeHandle { fn drop(&mut self) { // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed // after this because we own it and are being dropped. unsafe { assert_eq!(ffi::native_handle_close(self.0.as_ptr()), 0); assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); } } } impl From for NativeHandle { fn from(aidl_native_handle: AidlNativeHandle) -> Self { let fds = aidl_native_handle.fds.into_iter().map(OwnedFd::from).collect(); Self::new(fds, &aidl_native_handle.ints).unwrap() } } impl From for AidlNativeHandle { fn from(native_handle: NativeHandle) -> Self { let ints = native_handle.ints().to_owned(); let fds = native_handle.into_fds().into_iter().map(ParcelFileDescriptor::new).collect(); Self { ints, fds } } } // SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file // descriptors, which aren't tied to any particular thread. unsafe impl Send for NativeHandle {} // SAFETY: A `NativeHandle` can be used from different threads simultaneously, as is is just // integers and file descriptors. unsafe impl Sync for NativeHandle {} #[cfg(test)] mod test { use super::*; use std::fs::File; #[test] fn create_empty() { let handle = NativeHandle::new(vec![], &[]).unwrap(); assert_eq!(handle.fds().len(), 0); assert_eq!(handle.ints(), &[]); } #[test] fn create_with_ints() { let handle = NativeHandle::new(vec![], &[1, 2, 42]).unwrap(); assert_eq!(handle.fds().len(), 0); assert_eq!(handle.ints(), &[1, 2, 42]); } #[test] fn create_with_fd() { let file = File::open("/dev/null").unwrap(); let handle = NativeHandle::new(vec![file.into()], &[]).unwrap(); assert_eq!(handle.fds().len(), 1); assert_eq!(handle.ints(), &[]); } #[test] fn clone() { let file = File::open("/dev/null").unwrap(); let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); assert_eq!(original.ints(), &[42]); assert_eq!(original.fds().len(), 1); let cloned = original.clone(); drop(original); assert_eq!(cloned.ints(), &[42]); assert_eq!(cloned.fds().len(), 1); drop(cloned); } #[test] fn to_fds() { let file = File::open("/dev/null").unwrap(); let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); assert_eq!(original.ints(), &[42]); assert_eq!(original.fds().len(), 1); let fds = original.into_fds(); assert_eq!(fds.len(), 1); } #[test] fn to_aidl() { let file = File::open("/dev/null").unwrap(); let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); assert_eq!(original.ints(), &[42]); assert_eq!(original.fds().len(), 1); let aidl = AidlNativeHandle::from(original); assert_eq!(&aidl.ints, &[42]); assert_eq!(aidl.fds.len(), 1); } #[test] fn to_from_aidl() { let file = File::open("/dev/null").unwrap(); let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); assert_eq!(original.ints(), &[42]); assert_eq!(original.fds().len(), 1); let aidl = AidlNativeHandle::from(original); assert_eq!(&aidl.ints, &[42]); assert_eq!(aidl.fds.len(), 1); let converted_back = NativeHandle::from(aidl); assert_eq!(converted_back.ints(), &[42]); assert_eq!(converted_back.fds().len(), 1); } }