blob: c79b5b56e38813600d64bb8dd166acc9483ae965 [file]
/*
* Copyright (C) 2025 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.
*/
//! A wrapper around the `RawHandleSet`, that is aware of the handle wrapper type stored in the
//! raw handle set. The wrapper also enforces the limits on ports and connections per TA.
use crate::raw::{service_handle::PortWrapper, HandleType, RawHandleSet, ServiceHandle};
use crate::{
ConnectResult, Handle, MessageResult, PortCfg, Result, TipcError, UnbufferedService, Uuid,
};
use alloc::sync::Arc;
use log::error;
use std::collections::vec_deque::VecDeque;
use std::sync::Mutex;
use trusty_std::TryClone;
/// Encapsulate a work item to call on_connect on a given service
pub struct ToConnect<S: UnbufferedService> {
handle: Handle,
service: Arc<S>,
port: PortCfg,
// TODO: This should be removed in the long term
uuid: Uuid,
}
impl<S: UnbufferedService> ToConnect<S> {
/// Constructor
pub fn new(handle: Handle, service: Arc<S>, port: PortCfg, uuid: Uuid) -> Self {
Self { handle, service, port, uuid }
}
/// Handle on_connect work item
pub fn do_on_connect(&self) -> Result<ConnectResult<S::Connection>> {
self.service.on_connect(&self.port, &self.handle, &self.uuid)
}
}
/// Instructions about the work to do
pub enum WorkToDo<S: UnbufferedService> {
Connect(ToConnect<S>),
}
/// A wrapper around the `RawHandleSet` exposed to the TAs and the event loop runner.
pub struct HandleSetWrapper<S: UnbufferedService> {
// work queue should be emptied before waiting on the handle set
work_queue: Mutex<VecDeque<WorkToDo<S>>>,
handle_set: Mutex<RawHandleSet<ServiceHandle<S>>>,
}
impl<S: UnbufferedService> HandleSetWrapper<S> {
pub fn new() -> Result<Self> {
Ok(Self {
work_queue: Mutex::new(VecDeque::<WorkToDo<S>>::new()),
handle_set: Mutex::new(RawHandleSet::<ServiceHandle<S>>::new()?),
})
}
fn handle_work_queue(&self) -> Result<()> {
let mut wq = self.work_queue.lock().unwrap();
while let Some(work_item) = wq.pop_front() {
let WorkToDo::Connect(to_connect) = work_item;
match to_connect.do_on_connect()? {
ConnectResult::Accept(conn) => {
return self
.add_connection(conn, to_connect.handle, to_connect.service)
.map(|_| ());
}
ConnectResult::CloseConnection => {
error!("Connection closed by the service in on_connect.");
return Err(TipcError::ChannelClosed);
}
}
}
Ok(())
}
/// Waits on the raw handle set and returns the event cookie object and the event.
pub fn wait(&self, timeout: Option<u32>) -> Result<(Arc<ServiceHandle<S>>, u32)> {
self.handle_work_queue()?;
let hs = self.handle_set.lock().unwrap();
hs.wait(timeout)
}
/// Add a port type connection to the handle set wrapper
pub fn add_port(&self, cfg: &PortCfg, service: Arc<S>) -> Result<PortWrapper<S>> {
// SAFETY: syscall, config path is borrowed and outlives the call.
// Return value is either a negative error code or a valid handle.
let rc = unsafe {
trusty_sys::port_create(
cfg.get_path().as_ptr(),
cfg.get_msg_queue_len(),
cfg.get_msg_max_size(),
cfg.get_flags(),
)
};
if rc < 0 {
Err(TipcError::from_uapi(rc))
} else {
let raw_handle_fd = rc as i32;
let handle = Handle::from_raw(raw_handle_fd)?;
let service_handle =
Arc::new(ServiceHandle::<S>::new_port_wrapper(cfg.try_clone()?, handle, service));
let hs = self.handle_set.lock().unwrap();
hs.register(service_handle.clone())?;
Ok(PortWrapper::new(self, service_handle))
}
}
/// Dispatch an event on a port handle
pub fn handle_connect(&self, service_handle: Arc<ServiceHandle<S>>) -> Result<()> {
if let HandleType::Port(cfg) = &service_handle.ty {
let mut peer = Uuid::from_bytes(&[0; Uuid::UUID_BYTE_LEN]);
// SAFETY: syscall. The port owns its handle, so it is still valid as
// a raw fd. The peer structure outlives this call and is mutably
// borrowed by the call to initialize the structure's data.
let rc = unsafe {
trusty_sys::accept(service_handle.handle.as_raw_fd(), peer.as_mut_ptr()) as i32
};
let connection_handle = Handle::from_raw(rc)?;
// Check against access control list if we were given one
if let Some(uuids) = cfg.get_uuid_allow_list() {
if !uuids.contains(&peer) {
error!("UUID {peer:?} isn't supported.\n");
return Err(TipcError::SystemError(trusty_sys::Error::NotAllowed));
}
}
let connect_result =
service_handle.service.on_connect(&cfg, &connection_handle, &peer)?;
if let ConnectResult::Accept(conn) = connect_result {
self.add_connection(conn, connection_handle, Arc::clone(&service_handle.service))
.map(|_| ())
} else {
error!("Connection closed");
return Err(TipcError::ChannelClosed);
}
} else {
error!("A port type handle is expected. Received: {:?}", &service_handle.ty);
return Err(TipcError::InvalidData);
}
}
/// Register a connection handle in the handle set
pub fn add_connection(
&self,
connection: S::Connection,
handle: Handle,
service: Arc<S>,
) -> Result<()> {
let service_handle =
ServiceHandle::<S>::new_connection_wrapper(handle, service, connection);
let hs = self.handle_set.lock().unwrap();
hs.register(Arc::new(service_handle))
}
/// Dispatch an event on a port handle
pub fn handle_message(&self, service_handle: Arc<ServiceHandle<S>>) -> Result<MessageResult> {
if let HandleType::Connection(conn) = &service_handle.ty {
service_handle.service.on_message(conn, &service_handle.handle, &mut [])
} else {
error!("A connection type handle is expected. Received: {:?}", &service_handle.ty);
return Err(TipcError::InvalidData);
}
}
/// Dispatch a connection close event
pub fn handle_disconnect(&self, service_handle: Arc<ServiceHandle<S>>) -> Result<()> {
if let HandleType::Connection(conn) = &service_handle.ty {
service_handle.service.on_disconnect(conn)
} else {
error!("A connection type handle is expected. Received: {:?}", &service_handle.ty);
return Err(TipcError::InvalidData);
}
self.remove(service_handle)?;
Ok(())
}
/// Remove a handle from the handle set
pub fn remove(&self, service_handle: Arc<ServiceHandle<S>>) -> Result<()> {
let hs = self.handle_set.lock().unwrap();
hs.remove(service_handle)
}
/// Remove a handle from the handle set
pub fn remove_raw(&self, handle: i32) -> Result<Arc<ServiceHandle<S>>> {
let hs = self.handle_set.lock().unwrap();
hs.remove_raw(handle)
}
/// Add a work item
pub fn add_work(&self, todo: WorkToDo<S>) {
let mut wq = self.work_queue.lock().unwrap();
wq.push_back(todo);
}
}
#[cfg(test)]
mod test {
use crate::handle::test::{first_free_handle_index, MAX_USER_HANDLES};
use crate::raw::{raw_handle_set::Handler, HandleSetWrapper};
use crate::{
ConnectResult, Handle, MessageResult, PortCfg, Result, Service, TipcError,
UnbufferedService, Uuid,
};
use alloc::sync::Arc;
use test::{expect, expect_eq};
use trusty_std::ffi::{CString, FallibleCString as _};
use trusty_sys::Error;
const SRV_PATH_BASE: &str = "com.android.ipc-raw-unittest";
/// Maximum length of port path name
const MAX_PORT_PATH_LEN: usize = 64;
/// Maximum number of buffers per port
const MAX_PORT_BUF_NUM: u32 = 64;
/// Maximum size of port buffer
const MAX_PORT_BUF_SIZE: u32 = 4096;
#[test]
fn port_create_negative() {
let path = [0u8; 0];
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let service = Arc::new(());
let cfg = PortCfg::new_raw(CString::try_new(&path[..]).unwrap());
let err = handle_set_wrapper.add_port(&cfg, service.clone()).err();
expect_eq!(err, Some(TipcError::SystemError(Error::InvalidArgs)), "empty server path");
let mut path = format!("{}.port", SRV_PATH_BASE);
let cfg = PortCfg::new(&path).unwrap().msg_queue_len(0);
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::InvalidArgs)),
"no buffers",
);
let cfg = PortCfg::new(&path).unwrap().msg_max_size(0);
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::InvalidArgs)),
"zero buffer size",
);
let cfg = PortCfg::new(&path).unwrap().msg_queue_len(MAX_PORT_BUF_NUM * 100);
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::InvalidArgs)),
"large number of buffers",
);
let cfg = PortCfg::new(&path).unwrap().msg_max_size(MAX_PORT_BUF_SIZE * 100);
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::InvalidArgs)),
"large buffers size",
);
while path.len() < MAX_PORT_PATH_LEN + 16 {
path.push('a');
}
let cfg = PortCfg::new(&path).unwrap();
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::InvalidArgs)),
"path is too long",
);
}
#[test]
fn port_create() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let mut ports = Vec::new();
let service = Arc::new(());
for i in first_free_handle_index()..MAX_USER_HANDLES - 2 {
let path = format!("{}.port.{}{}", SRV_PATH_BASE, "test", i);
let cfg = PortCfg::new(path).unwrap();
let result = handle_set_wrapper.add_port(&cfg, service.clone());
ports.push(result.unwrap());
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::AlreadyExists)),
"collide with existing port"
);
}
// Creating one more port should succeed
let path = format!("{}.port.{}{}", SRV_PATH_BASE, "test", MAX_USER_HANDLES - 1);
let cfg = PortCfg::new(path).unwrap();
let result = handle_set_wrapper.add_port(&cfg, service.clone());
expect!(result.is_ok(), "create one more port");
// but creating colliding port should fail with different error code
// because we actually exceeded max number of handles instead of
// colliding with an existing path
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::NoResources)),
"collide with existing port",
);
let path = format!("{}.port.{}{}", SRV_PATH_BASE, "test", MAX_USER_HANDLES);
let cfg = PortCfg::new(path).unwrap();
expect_eq!(
handle_set_wrapper.add_port(&cfg, service.clone()).err(),
Some(TipcError::SystemError(Error::NoResources)),
"max number of ports reached",
);
}
#[test]
fn add_port() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let path = format!("{}.port.{}", SRV_PATH_BASE, "test");
let cfg = PortCfg::new(path).unwrap();
let service = Arc::new(());
{
let port_wrapper = handle_set_wrapper.add_port(&cfg, service.clone()).unwrap();
expect_eq!(Arc::strong_count(&port_wrapper.service_handle), 2);
expect_eq!(Arc::strong_count(&service), 2);
}
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn add_port_drop_wrapper() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let path = format!("{}.port.{}", SRV_PATH_BASE, "test");
let cfg = PortCfg::new(path).unwrap();
let service = Arc::new(());
{
// This line ignores the returned wrapper, which immediately drops it.
handle_set_wrapper.add_port(&cfg, service.clone()).unwrap();
expect_eq!(Arc::strong_count(&service), 1);
}
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn add_connection() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let path = format!("{}.port.{}", SRV_PATH_BASE, "test");
let cfg = PortCfg::new(path).unwrap();
let service = Arc::new(());
// SAFETY: syscall, `cfg` is a local and outlives the call.
// The return value is either a negative error code or a valid handle.
let rc = unsafe {
trusty_sys::port_create(
cfg.get_path().as_ptr(),
cfg.get_msg_queue_len(),
cfg.get_msg_max_size(),
cfg.get_flags(),
)
};
expect!(rc >= 0, "created the connection handle");
let handle_fd = rc as i32;
let handle = Handle::from_raw(handle_fd).unwrap();
handle_set_wrapper.add_connection((), handle, service.clone()).unwrap();
expect_eq!(Arc::strong_count(&service), 2);
expect!(handle_set_wrapper.remove_raw(handle_fd).is_ok(), "removed the connection handle");
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn add_connection_invalid_handle() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let handle = Handle::from_raw(1063).unwrap();
let service = Arc::new(());
expect_eq!(
Err(TipcError::SystemError(trusty_sys::Error::NotFound)),
handle_set_wrapper.add_connection((), handle, service.clone()),
"invalid handle"
);
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn remove() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let path = format!("{}.port.{}", SRV_PATH_BASE, "test");
let cfg = PortCfg::new(path).unwrap();
let service = Arc::new(());
{
let port_wrapper = handle_set_wrapper.add_port(&cfg, service.clone()).unwrap();
expect_eq!(Arc::strong_count(&port_wrapper.service_handle), 2);
expect_eq!(Arc::strong_count(&service), 2);
expect!(handle_set_wrapper.remove(port_wrapper.service_handle.clone()).is_ok());
expect_eq!(Arc::strong_count(&port_wrapper.service_handle), 1);
expect_eq!(
Err(TipcError::SystemError(trusty_sys::Error::NotFound)),
handle_set_wrapper.remove(port_wrapper.service_handle.clone()),
"handle already removed"
);
}
// `port_wrapper.service_handle` gets removed one more time here
// when port_wrapper is dropped. This emits an error message.
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn remove_raw() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let path = format!("{}.port.{}", SRV_PATH_BASE, "test");
let cfg = PortCfg::new(path).unwrap();
let service = Arc::new(());
{
let port_wrapper = handle_set_wrapper.add_port(&cfg, service.clone()).unwrap();
expect_eq!(Arc::strong_count(&port_wrapper.service_handle), 2);
expect_eq!(Arc::strong_count(&service), 2);
expect!(handle_set_wrapper
.remove_raw(port_wrapper.service_handle.get_raw_fd_id())
.is_ok());
expect_eq!(Arc::strong_count(&port_wrapper.service_handle), 1);
expect_eq!(
Err(TipcError::SystemError(trusty_sys::Error::NotFound)),
handle_set_wrapper.remove(port_wrapper.service_handle.clone())
);
}
// `port_wrapper.service_handle` gets removed one more time here
// when port_wrapper is dropped. This emits an error message.
expect_eq!(Arc::strong_count(&service), 1);
}
#[test]
fn wait_on_port() {
let handle_set_wrapper = HandleSetWrapper::<()>::new().unwrap();
let mut ports = Vec::new();
let service = Arc::new(());
for i in first_free_handle_index()..MAX_USER_HANDLES - 1 {
let path = format!("{}.port.{}{}", SRV_PATH_BASE, "test", i);
let cfg = PortCfg::new(path).unwrap();
let result = handle_set_wrapper.add_port(&cfg, service.clone());
ports.push(result.unwrap());
}
expect_eq!(
handle_set_wrapper.wait(Some(0)).err(),
Some(TipcError::SystemError(Error::TimedOut)),
"zero timeout"
);
expect_eq!(
handle_set_wrapper.wait(Some(100)).err(),
Some(TipcError::SystemError(Error::TimedOut)),
"non zero timeout"
);
}
struct Service1;
impl Service for Service1 {
type Connection = ();
type Message = ();
fn on_connect(
&self,
_port: &PortCfg,
_handle: &Handle,
_peer: &Uuid,
) -> Result<ConnectResult<Self::Connection>> {
Ok(ConnectResult::Accept(()))
}
fn on_message(
&self,
_connection: &Self::Connection,
handle: &Handle,
_msg: Self::Message,
) -> Result<MessageResult> {
handle.send(&1i32)?;
Ok(MessageResult::MaintainConnection)
}
}
struct Service2;
impl Service for Service2 {
type Connection = ();
type Message = ();
fn on_connect(
&self,
_port: &PortCfg,
_handle: &Handle,
_peer: &Uuid,
) -> Result<ConnectResult<Self::Connection>> {
Ok(ConnectResult::Accept(()))
}
fn on_message(
&self,
_connection: &Self::Connection,
handle: &Handle,
_msg: Self::Message,
) -> Result<MessageResult> {
handle.send(&2i32)?;
Ok(MessageResult::MaintainConnection)
}
}
enum WrapperService {
Service1(Service1),
Service2(Service2),
}
impl UnbufferedService for WrapperService {
type Connection = ();
fn on_connect(
&self,
port: &PortCfg,
handle: &Handle,
peer: &Uuid,
) -> Result<ConnectResult<Self::Connection>> {
match self {
Self::Service1(service_1) => {
match UnbufferedService::on_connect(service_1, port, handle, peer) {
Ok(conn_result) => match conn_result {
ConnectResult::Accept(conn) => Ok(ConnectResult::Accept(conn.into())),
ConnectResult::CloseConnection => Ok(ConnectResult::CloseConnection),
},
Err(e) => Err(e),
}
}
Self::Service2(service_2) => {
match UnbufferedService::on_connect(service_2, port, handle, peer) {
Ok(conn_result) => match conn_result {
ConnectResult::Accept(conn) => Ok(ConnectResult::Accept(conn.into())),
ConnectResult::CloseConnection => Ok(ConnectResult::CloseConnection),
},
Err(e) => Err(e),
}
}
}
}
fn on_message(
&self,
connection: &Self::Connection,
handle: &Handle,
buffer: &mut [u8],
) -> Result<MessageResult> {
match self {
Self::Service1(service_1) => UnbufferedService::on_message(
service_1,
connection.try_into().map_err(|_| TipcError::InvalidData)?,
handle,
buffer,
),
Self::Service2(service_2) => UnbufferedService::on_message(
service_2,
connection.try_into().map_err(|_| TipcError::InvalidData)?,
handle,
buffer,
),
}
}
}
#[test]
fn multiple_services() {
let handle_set_wrapper = Arc::new(HandleSetWrapper::new().unwrap());
let path_1 = format!("{}.port.{}", SRV_PATH_BASE, "testService1");
let cfg_1 = PortCfg::new(&path_1).unwrap();
let service_1 = Arc::new(WrapperService::Service1(Service1));
let path_2 = format!("{}.port.{}", SRV_PATH_BASE, "testService2");
let cfg_2 = PortCfg::new(&path_2).unwrap();
let service_2 = Arc::new(WrapperService::Service2(Service2));
{
let port_wrapper_1 =
handle_set_wrapper.add_port(&cfg_1, Arc::clone(&service_1)).unwrap();
let port_wrapper_2 =
handle_set_wrapper.add_port(&cfg_2, Arc::clone(&service_2)).unwrap();
expect_eq!(Arc::strong_count(&port_wrapper_1.service_handle), 2);
expect_eq!(Arc::strong_count(&service_1), 2);
expect_eq!(Arc::strong_count(&port_wrapper_2.service_handle), 2);
expect_eq!(Arc::strong_count(&service_2), 2);
}
expect_eq!(Arc::strong_count(&service_1), 1);
expect_eq!(Arc::strong_count(&service_2), 1);
}
}