diff options
author | 2023-08-08 02:52:23 +0000 | |
---|---|---|
committer | 2024-04-11 06:25:46 +0000 | |
commit | 2617c8434c52f453c1cbb627941bfc792dc5f359 (patch) | |
tree | 499b0ba78a08068ad3303778cde6b3edd80b57b4 | |
parent | a800ac7b2c26426e62e23a2e65325ebe3207e2a4 (diff) |
rpcbinder: Add Trusty server module
Add a Rust wrapper over RpcServerTrusty to allow
writing RPC Binder services in Rust.
Bug: 242243245
Test: trusty/vendor/google/aosp/scripts/build.py \
--test "boot-test:com.android.trusty.rust.binder_rpc_test.test" \
qemu-generic-arm64-test-debug
Change-Id: I55476c203c97f537a9f691cfafe72849e89a349b
-rw-r--r-- | libs/binder/rust/rpcbinder/Android.bp | 1 | ||||
-rw-r--r-- | libs/binder/rust/rpcbinder/src/lib.rs | 4 | ||||
-rw-r--r-- | libs/binder/rust/rpcbinder/src/server.rs | 162 | ||||
-rw-r--r-- | libs/binder/rust/rpcbinder/src/server/android.rs | 173 | ||||
-rw-r--r-- | libs/binder/rust/rpcbinder/src/server/trusty.rs | 161 | ||||
-rw-r--r-- | libs/binder/trusty/rust/rpcbinder/rules.mk | 2 |
6 files changed, 346 insertions, 157 deletions
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 535ce010f7..2e463451f9 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -18,6 +18,7 @@ rust_library { "libbinder_ndk_sys", "libbinder_rpc_unstable_bindgen_sys", "libbinder_rs", + "libcfg_if", "libdowncast_rs", "libforeign_types", "liblibc", diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 163f000ac8..7e5c9ddc35 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -16,10 +16,10 @@ //! API for RPC Binder services. -#[cfg(not(target_os = "trusty"))] mod server; mod session; +pub use server::RpcServer; #[cfg(not(target_os = "trusty"))] -pub use server::{RpcServer, RpcServerRef}; +pub use server::RpcServerRef; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index 6fda878d07..d6bdbd831f 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -14,160 +14,12 @@ * limitations under the License. */ -use crate::session::FileDescriptorTransportMode; -use binder::{unstable_api::AsNative, SpIBinder}; -use binder_rpc_unstable_bindgen::ARpcServer; -use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; -use std::io::{Error, ErrorKind}; -use std::os::unix::io::{IntoRawFd, OwnedFd}; - -foreign_type! { - type CType = binder_rpc_unstable_bindgen::ARpcServer; - fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; - - /// A type that represents a foreign instance of RpcServer. - #[derive(Debug)] - pub struct RpcServer; - /// A borrowed RpcServer. - pub struct RpcServerRef; -} - -/// SAFETY: The opaque handle can be cloned freely. -unsafe impl Send for RpcServer {} -/// SAFETY: The underlying C++ RpcServer class is thread-safe. -unsafe impl Sync for RpcServer {} - -impl RpcServer { - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// vsock port. Only connections from the given CID are accepted. - /// - // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. - // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. - pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( - service, cid, port, - )) - } - } - - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// socket file descriptor. The socket should be bound to an address before calling this - /// function. - pub fn new_bound_socket( - mut service: SpIBinder, - socket_fd: OwnedFd, - ) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - // The server takes ownership of the socket FD. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( - service, - socket_fd.into_raw_fd(), - )) - } - } - - /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket - /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix - /// domain sockets, pass one to the server and the other to the client. Multiple client session - /// can be created from the client end of the pair. - pub fn new_unix_domain_bootstrap( - mut service: SpIBinder, - bootstrap_fd: OwnedFd, - ) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - // The server takes ownership of the bootstrap FD. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( - service, - bootstrap_fd.into_raw_fd(), - )) - } - } - - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// IP address and port. - pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> { - let address = match CString::new(address) { - Ok(s) => s, - Err(e) => { - log::error!("Cannot convert {} to CString. Error: {:?}", address, e); - return Err(Error::from(ErrorKind::InvalidInput)); - } - }; - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet( - service, - address.as_ptr(), - port, - )) - } - } - - unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { - if ptr.is_null() { - return Err(Error::new(ErrorKind::Other, "Failed to start server")); - } - // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not - // null. - Ok(unsafe { RpcServer::from_ptr(ptr) }) - } -} - -impl RpcServerRef { - /// Sets the list of file descriptor transport modes supported by this server. - pub fn set_supported_file_descriptor_transport_modes( - &self, - modes: &[FileDescriptorTransportMode], - ) { - // SAFETY: Does not keep the pointer after returning does, nor does it - // read past its boundary. Only passes the 'self' pointer as an opaque handle. - unsafe { - binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( - self.as_ptr(), - modes.as_ptr(), - modes.len(), - ) - } - } - - /// Starts a new background thread and calls join(). Returns immediately. - pub fn start(&self) { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; - } - - /// Joins the RpcServer thread. The call blocks until the server terminates. - /// This must be called from exactly one thread. - pub fn join(&self) { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; - } - - /// Shuts down the running RpcServer. Can be called multiple times and from - /// multiple threads. Called automatically during drop(). - pub fn shutdown(&self) -> Result<(), Error> { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { - Ok(()) - } else { - Err(Error::from(ErrorKind::UnexpectedEof)) - } +cfg_if::cfg_if! { + if #[cfg(target_os = "trusty")] { + mod trusty; + pub use trusty::*; + } else { + mod android; + pub use android::*; } } diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs new file mode 100644 index 0000000000..ad0365b505 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server/android.rs @@ -0,0 +1,173 @@ +/* + * 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 crate::session::FileDescriptorTransportMode; +use binder::{unstable_api::AsNative, SpIBinder}; +use binder_rpc_unstable_bindgen::ARpcServer; +use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::ffi::CString; +use std::io::{Error, ErrorKind}; +use std::os::unix::io::{IntoRawFd, OwnedFd}; + +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcServer; + fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; + + /// A type that represents a foreign instance of RpcServer. + #[derive(Debug)] + pub struct RpcServer; + /// A borrowed RpcServer. + pub struct RpcServerRef; +} + +/// SAFETY: The opaque handle can be cloned freely. +unsafe impl Send for RpcServer {} +/// SAFETY: The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} + +impl RpcServer { + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// vsock port. Only connections from the given CID are accepted. + /// + // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. + // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. + pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( + service, cid, port, + )) + } + } + + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// socket file descriptor. The socket should be bound to an address before calling this + /// function. + pub fn new_bound_socket( + mut service: SpIBinder, + socket_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // The server takes ownership of the socket FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( + service, + socket_fd.into_raw_fd(), + )) + } + } + + /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket + /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix + /// domain sockets, pass one to the server and the other to the client. Multiple client session + /// can be created from the client end of the pair. + pub fn new_unix_domain_bootstrap( + mut service: SpIBinder, + bootstrap_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // The server takes ownership of the bootstrap FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( + service, + bootstrap_fd.into_raw_fd(), + )) + } + } + + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// IP address and port. + pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> { + let address = match CString::new(address) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", address, e); + return Err(Error::from(ErrorKind::InvalidInput)); + } + }; + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet( + service, + address.as_ptr(), + port, + )) + } + } + + unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { + if ptr.is_null() { + return Err(Error::new(ErrorKind::Other, "Failed to start server")); + } + // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not + // null. + Ok(unsafe { RpcServer::from_ptr(ptr) }) + } +} + +impl RpcServerRef { + /// Sets the list of file descriptor transport modes supported by this server. + pub fn set_supported_file_descriptor_transport_modes( + &self, + modes: &[FileDescriptorTransportMode], + ) { + // SAFETY: Does not keep the pointer after returning does, nor does it + // read past its boundary. Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( + self.as_ptr(), + modes.as_ptr(), + modes.len(), + ) + } + } + + /// Starts a new background thread and calls join(). Returns immediately. + pub fn start(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; + } + + /// Joins the RpcServer thread. The call blocks until the server terminates. + /// This must be called from exactly one thread. + pub fn join(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; + } + + /// Shuts down the running RpcServer. Can be called multiple times and from + /// multiple threads. Called automatically during drop(). + pub fn shutdown(&self) -> Result<(), Error> { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { + Ok(()) + } else { + Err(Error::from(ErrorKind::UnexpectedEof)) + } + } +} diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs new file mode 100644 index 0000000000..fe45decf25 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 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 binder::{unstable_api::AsNative, SpIBinder}; +use libc::size_t; +use std::ffi::{c_char, c_void}; +use std::ptr; +use tipc::{ConnectResult, Handle, MessageResult, PortCfg, TipcError, UnbufferedService, Uuid}; + +pub trait PerSessionCallback: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {} +impl<T> PerSessionCallback for T where T: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {} + +pub struct RpcServer { + inner: *mut binder_rpc_server_bindgen::ARpcServerTrusty, +} + +/// SAFETY: The opaque handle points to a heap allocation +/// that should be process-wide and not tied to the current thread. +unsafe impl Send for RpcServer {} +/// SAFETY: The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} + +impl Drop for RpcServer { + fn drop(&mut self) { + // SAFETY: `ARpcServerTrusty_delete` is the correct destructor to call + // on pointers returned by `ARpcServerTrusty_new`. + unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_delete(self.inner); + } + } +} + +impl RpcServer { + /// Allocates a new RpcServer object. + pub fn new(service: SpIBinder) -> RpcServer { + Self::new_per_session(move |_uuid| Some(service.clone())) + } + + /// Allocates a new per-session RpcServer object. + /// + /// Per-session objects take a closure that gets called once + /// for every new connection. The closure gets the UUID of + /// the peer and can accept or reject that connection. + pub fn new_per_session<F: PerSessionCallback>(f: F) -> RpcServer { + // SAFETY: Takes ownership of the returned handle, which has correct refcount. + let inner = unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_newPerSession( + Some(per_session_callback_wrapper::<F>), + Box::into_raw(Box::new(f)).cast(), + Some(per_session_callback_deleter::<F>), + ) + }; + RpcServer { inner } + } +} + +unsafe extern "C" fn per_session_callback_wrapper<F: PerSessionCallback>( + uuid_ptr: *const c_void, + len: size_t, + cb_ptr: *mut c_char, +) -> *mut binder_rpc_server_bindgen::AIBinder { + // SAFETY: This callback should only get called while the RpcServer is alive. + let cb = unsafe { &mut *cb_ptr.cast::<F>() }; + + if len != std::mem::size_of::<Uuid>() { + return ptr::null_mut(); + } + + // SAFETY: On the previous lines we check that we got exactly the right amount of bytes. + let uuid = unsafe { + let mut uuid = std::mem::MaybeUninit::<Uuid>::uninit(); + uuid.as_mut_ptr().copy_from(uuid_ptr.cast(), 1); + uuid.assume_init() + }; + + cb(uuid).map_or_else(ptr::null_mut, |b| { + // Prevent AIBinder_decStrong from being called before AIBinder_toPlatformBinder. + // The per-session callback in C++ is supposed to call AIBinder_decStrong on the + // pointer we return here. + std::mem::ManuallyDrop::new(b).as_native_mut().cast() + }) +} + +unsafe extern "C" fn per_session_callback_deleter<F: PerSessionCallback>(cb: *mut c_char) { + // SAFETY: shared_ptr calls this to delete the pointer we gave it. + // It should only get called once the last shared reference goes away. + unsafe { + drop(Box::<F>::from_raw(cb.cast())); + } +} + +pub struct RpcServerConnection { + ctx: *mut c_void, +} + +impl Drop for RpcServerConnection { + fn drop(&mut self) { + // We do not need to close handle_fd since we do not own it. + unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_handleChannelCleanup(self.ctx); + } + } +} + +impl UnbufferedService for RpcServer { + type Connection = RpcServerConnection; + + fn on_connect( + &self, + _port: &PortCfg, + handle: &Handle, + peer: &Uuid, + ) -> tipc::Result<ConnectResult<Self::Connection>> { + let mut conn = RpcServerConnection { ctx: std::ptr::null_mut() }; + let rc = unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_handleConnect( + self.inner, + handle.as_raw_fd(), + peer.as_ptr().cast(), + &mut conn.ctx, + ) + }; + if rc < 0 { + Err(TipcError::from_uapi(rc.into())) + } else { + Ok(ConnectResult::Accept(conn)) + } + } + + fn on_message( + &self, + conn: &Self::Connection, + _handle: &Handle, + buffer: &mut [u8], + ) -> tipc::Result<MessageResult> { + assert!(buffer.is_empty()); + let rc = unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleMessage(conn.ctx) }; + if rc < 0 { + Err(TipcError::from_uapi(rc.into())) + } else { + Ok(MessageResult::MaintainConnection) + } + } + + fn on_disconnect(&self, conn: &Self::Connection) { + unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleDisconnect(conn.ctx) }; + } +} diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk index 76f3b9401f..97f5c03cba 100644 --- a/libs/binder/trusty/rust/rpcbinder/rules.mk +++ b/libs/binder/trusty/rust/rpcbinder/rules.mk @@ -28,6 +28,8 @@ MODULE_LIBRARY_DEPS += \ $(LIBBINDER_DIR)/trusty/rust \ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_server_bindgen \ + external/rust/crates/cfg-if \ external/rust/crates/foreign-types \ trusty/user/base/lib/tipc/rust \ trusty/user/base/lib/trusty-sys \ |