Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 233b105e authored by Hasini Gunasinghe's avatar Hasini Gunasinghe
Browse files

Add support for Trusty RpcServer to accept three types of client ids

Background:
- Trusty RpcServer implements the UnbufferedService trait whose methods
  expect the client identifier to be of type Uuid.
- However, with the VM-TZ communication enabled by the TZ reduction
  project, the clients from the VMs  can be identified with a VM-ID or
  a client sequence number.
- In ag/33192048, we introduce a new method to the UnbufferedService
  trait to accept an enum with three variants representing the three
  types of client identifiers.

This CL introduces the following changes:
- Update the C++ code for Trusty RpcServer to accept and pass on a
  pointer to a tagged client id and the length, instead of a pointer to
  a Uuid.
- Implement the new method: "on_new_connection" added to the
  UnbufferedService trait and update the implementation of the existing
  method "on_connect" to pass a pointer to a tagged client id and length
  to the C++ code.
- Introduce a new constructor for the Trusty RpcServer, which takes a
  new call back that accepts the new enum for the client identifier.
- Add a FFI-wrapper to invoke the new callback from the C++ code, and
  update the FFI-wrapper for the existing callback to extract the Uuid
  from a tagged client id data.

Bug: 401776482
Flag: EXEMPT, opt-in is controlled by TRUSTY_SYSTEM_VM
Test: ./build-root/build-qemu-generic-arm64-test-debug/run --headless
    --boot-test "com.android.trusty.rust.authmgr_be_lib.test"
Change-Id: I67b0edd036ae50cc067db5d9e4844e3bff26f9fe
parent 63d49491
Loading
Loading
Loading
Loading
+80 −15
Original line number Diff line number Diff line
@@ -14,14 +14,28 @@
 * limitations under the License.
 */

use alloc::boxed::Box;
use binder::{unstable_api::AsNative, SpIBinder};
use libc::size_t;
use log::error;
use std::ffi::{c_char, c_void};
use std::ptr;
use tipc::{ConnectResult, Handle, MessageResult, PortCfg, TipcError, UnbufferedService, Uuid};
use tipc::{
    ClientIdentifier, 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 {}
/// Trait alias for the callback passed into the per-session constructor of the RpcServer.
/// Note: this is used in this file only, although it is marked as pub to be able to be used in
/// the definition of the pub constructor.
pub trait PerSessionCallback:
    Fn(ClientIdentifier) -> Option<SpIBinder> + Send + Sync + 'static
{
}
impl<T> PerSessionCallback for T where
    T: Fn(ClientIdentifier) -> Option<SpIBinder> + Send + Sync + 'static
{
}

pub struct RpcServer {
    inner: *mut binder_rpc_server_bindgen::ARpcServerTrusty,
@@ -52,7 +66,7 @@ impl RpcServer {
    /// 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
    /// for every new connection. The closure gets the `ClientIdentifier` 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.
@@ -68,25 +82,35 @@ impl RpcServer {
}

unsafe extern "C" fn per_session_callback_wrapper<F: PerSessionCallback>(
    uuid_ptr: *const c_void,
    client_id_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>() {
    if len < 1 {
        return ptr::null_mut();
    }
    // SAFETY: We have checked that the length is at least 1
    // We know that the pointer has not been freed at this point, because:
    // 1) The pointer is allocated in the call to: `on_connect` or `on_new_connection` in the
    //    implementation of the `UnbufferredService` trait for `RpcServer`.
    // 2) `on_connect` and `on_new_connection` invokes `ARpcServerTrusty_handleConnect`, immediately
    //    after the allocation.
    // 3) this callback is invoked in the callpath of `ARpcServerTrusty_handleConnect`.
    // We know that there is no concurrent mutable access to the pointer because it is allocated
    // and accessed in the same (single) process as per the callpath described above.
    let client_id_data = unsafe { std::slice::from_raw_parts(client_id_ptr.cast(), len) };
    let client_id = match ClientIdentifier::from_tagged_bytes(client_id_data) {
        Ok(c) => c,
        Err(_) => {
            error!("error in reconstructing the ClientIdentifier from pointer and length");
            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| {
    cb(client_id).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.
@@ -129,11 +153,21 @@ impl UnbufferedService for RpcServer {
        peer: &Uuid,
    ) -> tipc::Result<ConnectResult<Self::Connection>> {
        let mut conn = RpcServerConnection { ctx: std::ptr::null_mut() };
        let client_identifier = ClientIdentifier::UUID(peer.clone());
        let mut data = client_identifier.as_tagged_bytes();
        let len = data.len();
        // SAFETY: This unsafe block calls into a C++ function, which is considered safe, i.e. it
        // does not cause undefined behavior for valid inputs (see below), returns an integer which
        // indicates success or error, does not allocate or deallocate memory that Rust owns.
        // The inputs passed into the C++ function are valid: Trusty is single threaded, so there is
        // no concurrent access to `sef.inner`` and other inputs are not freed/deallocated until the
        // function returns. Correct length of the data pointed to by the pointer is passed in.
        let rc = unsafe {
            binder_rpc_server_bindgen::ARpcServerTrusty_handleConnect(
                self.inner,
                handle.as_raw_fd(),
                peer.as_ptr().cast(),
                data.as_mut_ptr() as *const c_void,
                len,
                &mut conn.ctx,
            )
        };
@@ -161,4 +195,35 @@ impl UnbufferedService for RpcServer {
    fn on_disconnect(&self, conn: &Self::Connection) {
        unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleDisconnect(conn.ctx) };
    }

    fn on_new_connection(
        &self,
        _port: &PortCfg,
        handle: &Handle,
        client_identifier: &ClientIdentifier,
    ) -> tipc::Result<ConnectResult<Self::Connection>> {
        let mut conn = RpcServerConnection { ctx: std::ptr::null_mut() };
        let mut data = client_identifier.as_tagged_bytes();
        let len = data.len();
        // SAFETY: This unsafe block calls into a C++ function, which is considered safe, i.e. it
        // does not cause undefined behavior for valid inputs (see below), returns an integer which
        // indicates success or error, does not allocate or deallocate memory that Rust owns.
        // The inputs passed into the C++ function are valid: Trusty is single threaded, so there is
        // no concurrent access to `sef.inner`` and other inputs are not freed/deallocated until the
        // function returns. Correct length of the data pointed to by the pointer is passed in.
        let rc = unsafe {
            binder_rpc_server_bindgen::ARpcServerTrusty_handleConnect(
                self.inner,
                handle.as_raw_fd(),
                data.as_mut_ptr() as *const c_void,
                len,
                &mut conn.ctx,
            )
        };
        if rc < 0 {
            Err(TipcError::from_uapi(rc.into()))
        } else {
            Ok(ConnectResult::Accept(conn))
        }
    }
}
+10 −6
Original line number Diff line number Diff line
@@ -97,11 +97,13 @@ RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::stri
int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer,
                                   void** ctx_p) {
    auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv));
    return handleConnectInternal(server->mRpcServer.get(), chan, peer, ctx_p);
    const void* uuid_ptr = static_cast<const void*>(peer);
    constexpr size_t uuidLen = sizeof(*peer);
    return handleConnectInternal(server->mRpcServer.get(), chan, uuid_ptr, uuidLen, ctx_p);
}

int RpcServerTrusty::handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer,
                                           void** ctx_p) {
int RpcServerTrusty::handleConnectInternal(RpcServer* rpcServer, handle_t chan,
                                           const void* addrData, size_t addrDataLen, void** ctx_p) {
    rpcServer->mShutdownTrigger = FdTrigger::make();
    rpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread();

@@ -137,10 +139,12 @@ int RpcServerTrusty::handleConnectInternal(RpcServer* rpcServer, handle_t chan,
    android::RpcTransportFd transportFd(std::move(clientFd));

    std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
    constexpr size_t addrLen = sizeof(*peer);
    memcpy(addr.data(), peer, addrLen);
    if (addrDataLen > RpcServer::kRpcAddressSize) {
        return ERR_BAD_LEN;
    }
    memcpy(addr.data(), addrData, addrDataLen);
    RpcServer::establishConnection(sp<RpcServer>::fromExisting(rpcServer), std::move(transportFd),
                                   addr, addrLen, joinFn);
                                   addr, addrDataLen, joinFn);

    return rc;
}
+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ struct ARpcServerTrusty* ARpcServerTrusty_newPerSession(struct AIBinder* (*)(con
                                                                             char*),
                                                        char*, void (*)(char*));
void ARpcServerTrusty_delete(struct ARpcServerTrusty*);
int ARpcServerTrusty_handleConnect(struct ARpcServerTrusty*, handle_t, const struct uuid*, void**);
int ARpcServerTrusty_handleConnect(struct ARpcServerTrusty*, handle_t, const void*, size_t, void**);
int ARpcServerTrusty_handleMessage(void*);
void ARpcServerTrusty_handleDisconnect(void*);
void ARpcServerTrusty_handleChannelCleanup(void*);
+15 −3
Original line number Diff line number Diff line
@@ -114,7 +114,19 @@ private:
                                                                                char*),
                                                                char*, void (*)(char*));
    friend void ::ARpcServerTrusty_delete(::ARpcServerTrusty*);
    friend int ::ARpcServerTrusty_handleConnect(::ARpcServerTrusty*, handle_t, const uuid*, void**);
    /**
     * @brief Handle the binder RPC connection.
     *
     * @param rstr  - RpcServer object
     * @param chan - handle to the connection
     * @param clientId - identifier of the client as tagged data
     * @param clientIdLen - length of the client identifier
     * @param ctx_p - connection context
     * @return int - error code
     */
    friend int ::ARpcServerTrusty_handleConnect(struct ARpcServerTrusty* rstr, handle_t chan,
                                                const void* clientId, size_t clientIdLen,
                                                void** ctx_p);
    friend int ::ARpcServerTrusty_handleMessage(void*);
    friend void ::ARpcServerTrusty_handleDisconnect(void*);
    friend void ::ARpcServerTrusty_handleChannelCleanup(void*);
@@ -130,8 +142,8 @@ private:
    static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx);
    static void handleChannelCleanup(void* ctx);

    static int handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer,
                                     void** ctx_p);
    static int handleConnectInternal(RpcServer* rpcServer, handle_t chan, const void* addrData,
                                     size_t addrDataLen, void** ctx_p);
    static int handleMessageInternal(void* ctx);
    static void handleDisconnectInternal(void* ctx);

+4 −3
Original line number Diff line number Diff line
@@ -72,9 +72,10 @@ void ARpcServerTrusty_delete(ARpcServerTrusty* rstr) {
    delete rstr;
}

int ARpcServerTrusty_handleConnect(ARpcServerTrusty* rstr, handle_t chan, const uuid* peer,
                                   void** ctx_p) {
    return RpcServerTrusty::handleConnectInternal(rstr->mRpcServer.get(), chan, peer, ctx_p);
int ARpcServerTrusty_handleConnect(ARpcServerTrusty* rstr, handle_t chan, const void* clientId,
                                   size_t clientIdLen, void** ctx_p) {
    return RpcServerTrusty::handleConnectInternal(rstr->mRpcServer.get(), chan, clientId,
                                                  clientIdLen, ctx_p);
}

int ARpcServerTrusty_handleMessage(void* ctx) {