Andrew Walbran | 9487efd | 2024-08-20 18:37:59 +0100 | [diff] [blame] | 1 | // Copyright (C) 2024 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
Andrew Walbran | 9639e12 | 2024-10-22 18:27:38 +0100 | [diff] [blame] | 15 | use android_hardware_common::{ |
| 16 | aidl::android::hardware::common::NativeHandle::NativeHandle as AidlNativeHandle, |
| 17 | binder::ParcelFileDescriptor, |
| 18 | }; |
Andrew Walbran | 0a3eb86 | 2024-09-18 13:49:04 +0100 | [diff] [blame] | 19 | use std::{ |
| 20 | ffi::c_int, |
| 21 | mem::forget, |
| 22 | os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, |
| 23 | ptr::NonNull, |
| 24 | }; |
Andrew Walbran | 9487efd | 2024-08-20 18:37:59 +0100 | [diff] [blame] | 25 | |
| 26 | /// Rust wrapper around `native_handle_t`. |
| 27 | /// |
| 28 | /// This owns the `native_handle_t` and its file descriptors, and will close them and free it when |
| 29 | /// it is dropped. |
| 30 | #[derive(Debug)] |
| 31 | pub struct NativeHandle(NonNull<ffi::native_handle_t>); |
| 32 | |
| 33 | impl NativeHandle { |
Andrew Walbran | 0a3eb86 | 2024-09-18 13:49:04 +0100 | [diff] [blame] | 34 | /// Creates a new `NativeHandle` with the given file descriptors and integer values. |
| 35 | /// |
| 36 | /// The `NativeHandle` will take ownership of the file descriptors and close them when it is |
| 37 | /// dropped. |
| 38 | pub fn new(fds: Vec<OwnedFd>, ints: &[c_int]) -> Option<Self> { |
| 39 | let fd_count = fds.len(); |
| 40 | // SAFETY: native_handle_create doesn't have any safety requirements. |
| 41 | let handle = unsafe { |
| 42 | ffi::native_handle_create(fd_count.try_into().unwrap(), ints.len().try_into().unwrap()) |
| 43 | }; |
| 44 | let handle = NonNull::new(handle)?; |
| 45 | for (i, fd) in fds.into_iter().enumerate() { |
| 46 | // SAFETY: `handle` must be valid because it was just created, and the array offset is |
| 47 | // within the bounds of what we allocated above. |
| 48 | unsafe { |
| 49 | *(*handle.as_ptr()).data.as_mut_ptr().add(i) = fd.into_raw_fd(); |
| 50 | } |
| 51 | } |
| 52 | for (i, value) in ints.iter().enumerate() { |
| 53 | // SAFETY: `handle` must be valid because it was just created, and the array offset is |
| 54 | // within the bounds of what we allocated above. Note that `data` is uninitialized |
| 55 | // until after this so we can't use `slice::from_raw_parts_mut` or similar to create a |
| 56 | // reference to it so we use raw pointers arithmetic instead. |
| 57 | unsafe { |
| 58 | *(*handle.as_ptr()).data.as_mut_ptr().add(fd_count + i) = *value; |
| 59 | } |
| 60 | } |
| 61 | // SAFETY: `handle` must be valid because it was just created. |
| 62 | unsafe { |
| 63 | ffi::native_handle_set_fdsan_tag(handle.as_ptr()); |
| 64 | } |
| 65 | Some(Self(handle)) |
| 66 | } |
| 67 | |
| 68 | /// Returns a borrowed view of all the file descriptors in this native handle. |
| 69 | pub fn fds(&self) -> Vec<BorrowedFd> { |
| 70 | self.data()[..self.fd_count()] |
| 71 | .iter() |
| 72 | .map(|fd| { |
| 73 | // SAFETY: The `native_handle_t` maintains ownership of the file descriptor so it |
| 74 | // won't be closed until this `NativeHandle` is destroyed. The `BorrowedFd` will |
| 75 | // have a lifetime constrained to that of `&self`, so it can't outlive it. |
| 76 | unsafe { BorrowedFd::borrow_raw(*fd) } |
| 77 | }) |
| 78 | .collect() |
| 79 | } |
| 80 | |
| 81 | /// Returns the integer values in this native handle. |
| 82 | pub fn ints(&self) -> &[c_int] { |
| 83 | &self.data()[self.fd_count()..] |
| 84 | } |
| 85 | |
| 86 | /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained. |
| 87 | pub fn into_fds(self) -> Vec<OwnedFd> { |
Ren-Pei Zeng | 5e2ebfb | 2024-10-23 08:31:33 +0000 | [diff] [blame] | 88 | // Unset FDSan tag since this `native_handle_t` is no longer the owner of the file |
| 89 | // descriptors after this function. |
| 90 | // SAFETY: Our wrapped `native_handle_t` pointer is always valid. |
| 91 | unsafe { |
| 92 | ffi::native_handle_unset_fdsan_tag(self.as_ref()); |
| 93 | } |
Andrew Walbran | 0a3eb86 | 2024-09-18 13:49:04 +0100 | [diff] [blame] | 94 | let fds = self.data()[..self.fd_count()] |
| 95 | .iter() |
| 96 | .map(|fd| { |
| 97 | // SAFETY: The `native_handle_t` has ownership of the file descriptor, and |
| 98 | // after this we destroy it without closing the file descriptor so we can take over |
| 99 | // ownership of it. |
| 100 | unsafe { OwnedFd::from_raw_fd(*fd) } |
| 101 | }) |
| 102 | .collect(); |
| 103 | |
| 104 | // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed |
| 105 | // after this because we own it and forget it. |
| 106 | unsafe { |
| 107 | assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); |
| 108 | } |
| 109 | // Don't drop self, as that would cause `native_handle_close` to be called and close the |
| 110 | // file descriptors. |
| 111 | forget(self); |
| 112 | fds |
| 113 | } |
| 114 | |
| 115 | /// Returns a reference to the underlying `native_handle_t`. |
| 116 | fn as_ref(&self) -> &ffi::native_handle_t { |
| 117 | // SAFETY: All the ways of creating a `NativeHandle` ensure that the `native_handle_t` is |
| 118 | // valid and initialised, and lives as long as the `NativeHandle`. We enforce Rust's |
| 119 | // aliasing rules by giving the reference a lifetime matching that of `&self`. |
| 120 | unsafe { self.0.as_ref() } |
| 121 | } |
| 122 | |
| 123 | /// Returns the number of file descriptors included in the native handle. |
| 124 | fn fd_count(&self) -> usize { |
| 125 | self.as_ref().numFds.try_into().unwrap() |
| 126 | } |
| 127 | |
| 128 | /// Returns the number of integer values included in the native handle. |
| 129 | fn int_count(&self) -> usize { |
| 130 | self.as_ref().numInts.try_into().unwrap() |
| 131 | } |
| 132 | |
| 133 | /// Returns a slice reference for all the used `data` field of the native handle, including both |
| 134 | /// file descriptors and integers. |
| 135 | fn data(&self) -> &[c_int] { |
| 136 | let total_count = self.fd_count() + self.int_count(); |
| 137 | // SAFETY: The data must have been initialised with this number of elements when the |
| 138 | // `NativeHandle` was created. |
| 139 | unsafe { self.as_ref().data.as_slice(total_count) } |
| 140 | } |
| 141 | |
Andrew Walbran | 9487efd | 2024-08-20 18:37:59 +0100 | [diff] [blame] | 142 | /// Wraps a raw `native_handle_t` pointer, taking ownership of it. |
| 143 | /// |
| 144 | /// # Safety |
| 145 | /// |
| 146 | /// `native_handle` must be a valid pointer to a `native_handle_t`, and must not be used |
| 147 | /// anywhere else after calling this method. |
| 148 | pub unsafe fn from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Self { |
| 149 | Self(native_handle) |
| 150 | } |
| 151 | |
| 152 | /// Creates a new `NativeHandle` wrapping a clone of the given `native_handle_t` pointer. |
| 153 | /// |
| 154 | /// Unlike [`from_raw`](Self::from_raw) this doesn't take ownership of the pointer passed in, so |
| 155 | /// the caller remains responsible for closing and freeing it. |
| 156 | /// |
| 157 | /// # Safety |
| 158 | /// |
| 159 | /// `native_handle` must be a valid pointer to a `native_handle_t`. |
| 160 | pub unsafe fn clone_from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Option<Self> { |
| 161 | // SAFETY: The caller promised that `native_handle` was valid. |
| 162 | let cloned = unsafe { ffi::native_handle_clone(native_handle.as_ptr()) }; |
| 163 | NonNull::new(cloned).map(Self) |
| 164 | } |
| 165 | |
| 166 | /// Returns a raw pointer to the wrapped `native_handle_t`. |
| 167 | /// |
| 168 | /// This is only valid as long as this `NativeHandle` exists, so shouldn't be stored. It mustn't |
| 169 | /// be closed or deleted. |
| 170 | pub fn as_raw(&self) -> NonNull<ffi::native_handle_t> { |
| 171 | self.0 |
| 172 | } |
| 173 | |
| 174 | /// Turns the `NativeHandle` into a raw `native_handle_t`. |
| 175 | /// |
| 176 | /// The caller takes ownership of the `native_handle_t` and its file descriptors, so is |
| 177 | /// responsible for closing and freeing it. |
| 178 | pub fn into_raw(self) -> NonNull<ffi::native_handle_t> { |
| 179 | let raw = self.0; |
| 180 | forget(self); |
| 181 | raw |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | impl Clone for NativeHandle { |
| 186 | fn clone(&self) -> Self { |
| 187 | // SAFETY: Our wrapped `native_handle_t` pointer is always valid. |
| 188 | unsafe { Self::clone_from_raw(self.0) }.expect("native_handle_clone returned null") |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | impl Drop for NativeHandle { |
| 193 | fn drop(&mut self) { |
| 194 | // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed |
| 195 | // after this because we own it and are being dropped. |
| 196 | unsafe { |
| 197 | assert_eq!(ffi::native_handle_close(self.0.as_ptr()), 0); |
| 198 | assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
Andrew Walbran | 9639e12 | 2024-10-22 18:27:38 +0100 | [diff] [blame] | 203 | impl From<AidlNativeHandle> for NativeHandle { |
| 204 | fn from(aidl_native_handle: AidlNativeHandle) -> Self { |
| 205 | let fds = aidl_native_handle.fds.into_iter().map(OwnedFd::from).collect(); |
| 206 | Self::new(fds, &aidl_native_handle.ints).unwrap() |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | impl From<NativeHandle> for AidlNativeHandle { |
| 211 | fn from(native_handle: NativeHandle) -> Self { |
| 212 | let ints = native_handle.ints().to_owned(); |
| 213 | let fds = native_handle.into_fds().into_iter().map(ParcelFileDescriptor::new).collect(); |
| 214 | Self { ints, fds } |
| 215 | } |
| 216 | } |
| 217 | |
Andrew Walbran | 9487efd | 2024-08-20 18:37:59 +0100 | [diff] [blame] | 218 | // SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file |
| 219 | // descriptors, which aren't tied to any particular thread. |
| 220 | unsafe impl Send for NativeHandle {} |
| 221 | |
| 222 | // SAFETY: A `NativeHandle` can be used from different threads simultaneously, as is is just |
| 223 | // integers and file descriptors. |
| 224 | unsafe impl Sync for NativeHandle {} |
Andrew Walbran | 0a3eb86 | 2024-09-18 13:49:04 +0100 | [diff] [blame] | 225 | |
| 226 | #[cfg(test)] |
| 227 | mod test { |
| 228 | use super::*; |
| 229 | use std::fs::File; |
| 230 | |
| 231 | #[test] |
| 232 | fn create_empty() { |
| 233 | let handle = NativeHandle::new(vec![], &[]).unwrap(); |
| 234 | assert_eq!(handle.fds().len(), 0); |
| 235 | assert_eq!(handle.ints(), &[]); |
| 236 | } |
| 237 | |
| 238 | #[test] |
| 239 | fn create_with_ints() { |
| 240 | let handle = NativeHandle::new(vec![], &[1, 2, 42]).unwrap(); |
| 241 | assert_eq!(handle.fds().len(), 0); |
| 242 | assert_eq!(handle.ints(), &[1, 2, 42]); |
| 243 | } |
| 244 | |
| 245 | #[test] |
| 246 | fn create_with_fd() { |
| 247 | let file = File::open("/dev/null").unwrap(); |
| 248 | let handle = NativeHandle::new(vec![file.into()], &[]).unwrap(); |
| 249 | assert_eq!(handle.fds().len(), 1); |
| 250 | assert_eq!(handle.ints(), &[]); |
| 251 | } |
| 252 | |
| 253 | #[test] |
| 254 | fn clone() { |
| 255 | let file = File::open("/dev/null").unwrap(); |
| 256 | let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); |
| 257 | assert_eq!(original.ints(), &[42]); |
| 258 | assert_eq!(original.fds().len(), 1); |
| 259 | |
| 260 | let cloned = original.clone(); |
| 261 | drop(original); |
| 262 | |
| 263 | assert_eq!(cloned.ints(), &[42]); |
| 264 | assert_eq!(cloned.fds().len(), 1); |
| 265 | |
| 266 | drop(cloned); |
| 267 | } |
Andrew Walbran | 9639e12 | 2024-10-22 18:27:38 +0100 | [diff] [blame] | 268 | |
| 269 | #[test] |
Ren-Pei Zeng | 5e2ebfb | 2024-10-23 08:31:33 +0000 | [diff] [blame] | 270 | fn to_fds() { |
| 271 | let file = File::open("/dev/null").unwrap(); |
| 272 | let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); |
| 273 | assert_eq!(original.ints(), &[42]); |
| 274 | assert_eq!(original.fds().len(), 1); |
| 275 | |
| 276 | let fds = original.into_fds(); |
| 277 | assert_eq!(fds.len(), 1); |
| 278 | } |
| 279 | |
| 280 | #[test] |
| 281 | fn to_aidl() { |
| 282 | let file = File::open("/dev/null").unwrap(); |
| 283 | let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); |
| 284 | assert_eq!(original.ints(), &[42]); |
| 285 | assert_eq!(original.fds().len(), 1); |
| 286 | |
| 287 | let aidl = AidlNativeHandle::from(original); |
| 288 | assert_eq!(&aidl.ints, &[42]); |
| 289 | assert_eq!(aidl.fds.len(), 1); |
| 290 | } |
| 291 | |
| 292 | #[test] |
Andrew Walbran | 9639e12 | 2024-10-22 18:27:38 +0100 | [diff] [blame] | 293 | fn to_from_aidl() { |
| 294 | let file = File::open("/dev/null").unwrap(); |
| 295 | let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); |
| 296 | assert_eq!(original.ints(), &[42]); |
| 297 | assert_eq!(original.fds().len(), 1); |
| 298 | |
| 299 | let aidl = AidlNativeHandle::from(original); |
| 300 | assert_eq!(&aidl.ints, &[42]); |
| 301 | assert_eq!(aidl.fds.len(), 1); |
| 302 | |
| 303 | let converted_back = NativeHandle::from(aidl); |
| 304 | assert_eq!(converted_back.ints(), &[42]); |
| 305 | assert_eq!(converted_back.fds().len(), 1); |
| 306 | } |
Andrew Walbran | 0a3eb86 | 2024-09-18 13:49:04 +0100 | [diff] [blame] | 307 | } |