Switch compsvc to use authfs_service
With authfs_service, we no longer need compsvc_worker.rs and authfs.rs
for the task setup. Now, for each request, compsvc can just request
FDs from authfs_service then pass to the task.
Also, fixed the integer type of remote FD to match ParcelFileDescriptor.
Bug: 194717985
Test: atest ComposHostTestCases
Change-Id: I8c0be106243778ac20e7cd96a778db4e34aef051
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index ae242de..14b520e 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -15,34 +15,35 @@
*/
//! compsvc is a service to run computational tasks in a PVM upon request. It is able to set up
-//! file descriptors backed by fd_server and pass the file descriptors to the actual tasks for
-//! read/write. The service also attempts to sandbox the execution so that one task cannot leak or
-//! impact future tasks.
-//!
-//! The current architecture / process hierarchy looks like:
-//! - compsvc (handle requests)
-//! - compsvc_worker (for environment setup)
-//! - authfs (fd translation)
-//! - actual task
+//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
+//! actual tasks.
use anyhow::Result;
use log::error;
use minijail::{self, Minijail};
-use std::path::PathBuf;
+use std::ffi::CString;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
use crate::signer::Signer;
+use authfs_aidl_interface::aidl::com::android::virt::fs::{
+ AuthFsConfig::AuthFsConfig, IAuthFs::IAuthFs, IAuthFsService::IAuthFsService,
+ InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
+};
+use authfs_aidl_interface::binder::ParcelFileDescriptor;
use compos_aidl_interface::aidl::com::android::compos::ICompService::{
BnCompService, ICompService,
};
use compos_aidl_interface::aidl::com::android::compos::Metadata::Metadata;
use compos_aidl_interface::binder::{
- BinderFeatures, Interface, Result as BinderResult, Status, StatusCode, Strong,
+ BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
};
-const WORKER_BIN: &str = "/apex/com.android.compos/bin/compsvc_worker";
+const AUTHFS_SERVICE_NAME: &str = "authfs_service";
-// TODO: Replace with a valid directory setup in the VM.
-const AUTHFS_MOUNTPOINT: &str = "/data/local/tmp";
+/// The number that represents the file descriptor number expecting by the task. The number may be
+/// meaningless in the current process.
+pub type PseudoRawFd = i32;
/// Constructs a binder object that implements ICompService. task_bin is the path to the binary that will
/// be run when execute() is called. If debuggable is true then stdout/stderr from the binary will be
@@ -52,83 +53,128 @@
debuggable: bool,
signer: Option<Box<dyn Signer>>,
) -> Strong<dyn ICompService> {
- let service = CompService {
- worker_bin: PathBuf::from(WORKER_BIN.to_owned()),
- task_bin,
- debuggable,
- signer,
- };
+ let service = CompService { task_bin: PathBuf::from(task_bin), debuggable, signer };
BnCompService::new_binder(service, BinderFeatures::default())
}
struct CompService {
- task_bin: String,
- worker_bin: PathBuf,
+ task_bin: PathBuf,
debuggable: bool,
#[allow(dead_code)] // TODO: Make use of this
signer: Option<Box<dyn Signer>>,
}
-impl CompService {
- fn run_worker_in_jail_and_wait(&self, args: &[String]) -> Result<(), minijail::Error> {
- let mut jail = Minijail::new()?;
-
- // TODO(b/185175567): New user and uid namespace when supported. Run as nobody.
- // New mount namespace to isolate the FUSE mount.
- jail.namespace_vfs();
-
- let inheritable_fds = if self.debuggable {
- vec![1, 2] // inherit/redirect stdout/stderr for debugging
- } else {
- vec![]
- };
- let _pid = jail.run(&self.worker_bin, &inheritable_fds, args)?;
- jail.wait()
- }
-
- fn build_worker_args(&self, args: &[String], metadata: &Metadata) -> Vec<String> {
- let mut worker_args = vec![
- WORKER_BIN.to_string(),
- "--authfs-root".to_string(),
- AUTHFS_MOUNTPOINT.to_string(),
- ];
- for annotation in &metadata.input_fd_annotations {
- worker_args.push("--in-fd".to_string());
- worker_args.push(format!("{}:{}", annotation.fd, annotation.file_size));
- }
- for annotation in &metadata.output_fd_annotations {
- worker_args.push("--out-fd".to_string());
- worker_args.push(annotation.fd.to_string());
- }
- if self.debuggable {
- worker_args.push("--debug".to_string());
- }
- worker_args.push("--".to_string());
-
- // Do not accept arbitrary code execution. We want to execute some specific task of this
- // service. Use the associated executable.
- worker_args.push(self.task_bin.clone());
- worker_args.extend_from_slice(&args[1..]);
- worker_args
- }
-}
-
impl Interface for CompService {}
impl ICompService for CompService {
fn execute(&self, args: &[String], metadata: &Metadata) -> BinderResult<i8> {
- let worker_args = self.build_worker_args(args, metadata);
+ // Mount authfs (via authfs_service).
+ let authfs_config = build_authfs_config(metadata);
+ let authfs = get_authfs_service()?.mount(&authfs_config)?;
- match self.run_worker_in_jail_and_wait(&worker_args) {
+ // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
+ // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
+ // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
+ // setup.
+ let fd_mapping =
+ open_authfs_files_for_fd_mapping(&authfs, &authfs_config).map_err(|e| {
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to create FDs on authfs: {:?}", e),
+ )
+ })?;
+
+ let jail =
+ spawn_jailed_task(&self.task_bin, args, fd_mapping, self.debuggable).map_err(|e| {
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to spawn the task: {:?}", e),
+ )
+ })?;
+ let jail_result = jail.wait();
+
+ // Be explicit about the lifetime, which should last at least until the task is finished.
+ drop(authfs);
+
+ match jail_result {
Ok(_) => Ok(0), // TODO(b/161471326): Sign the output on succeed.
Err(minijail::Error::ReturnCode(exit_code)) => {
error!("Task failed with exit code {}", exit_code);
Err(Status::from(StatusCode::FAILED_TRANSACTION))
}
Err(e) => {
- error!("Unexpected error: {}", e);
+ error!("Unexpected minijail error: {}", e);
Err(Status::from(StatusCode::UNKNOWN_ERROR))
}
}
}
}
+
+fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
+ Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
+}
+
+fn build_authfs_config(metadata: &Metadata) -> AuthFsConfig {
+ AuthFsConfig {
+ port: 3264, // TODO: support dynamic port
+ inputFdAnnotations: metadata
+ .input_fd_annotations
+ .iter()
+ .map(|x| InputFdAnnotation { fd: x.fd, fileSize: x.file_size })
+ .collect(),
+ outputFdAnnotations: metadata
+ .output_fd_annotations
+ .iter()
+ .map(|x| OutputFdAnnotation { fd: x.fd })
+ .collect(),
+ }
+}
+
+fn open_authfs_files_for_fd_mapping(
+ authfs: &Strong<dyn IAuthFs>,
+ config: &AuthFsConfig,
+) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
+ let mut fd_mapping = Vec::new();
+
+ let results: Result<Vec<_>> = config
+ .inputFdAnnotations
+ .iter()
+ .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
+ .collect();
+ fd_mapping.append(&mut results?);
+
+ let results: Result<Vec<_>> = config
+ .outputFdAnnotations
+ .iter()
+ .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
+ .collect();
+ fd_mapping.append(&mut results?);
+
+ Ok(fd_mapping)
+}
+
+fn spawn_jailed_task(
+ executable: &Path,
+ args: &[String],
+ fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
+ debuggable: bool,
+) -> Result<Minijail> {
+ // TODO(b/185175567): Run in a more restricted sandbox.
+ let jail = Minijail::new()?;
+
+ let mut preserve_fds = if debuggable {
+ // Inherit/redirect stdout/stderr for debugging, assuming no conflict
+ vec![(1, 1), (2, 2)]
+ } else {
+ vec![]
+ };
+
+ preserve_fds.extend(fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)));
+
+ let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
+ Ok(jail)
+}
+
+fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
+ Status::new_exception(exception, CString::new(message.as_ref()).as_deref().ok())
+}