blob: 14b520ee5a0d97b0c5c367cae6e806c41efa3d9b [file] [log] [blame]
Victor Hsieh272aa242021-02-01 14:19:20 -08001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! compsvc is a service to run computational tasks in a PVM upon request. It is able to set up
Victor Hsiehebb1d902021-08-06 13:00:18 -070018//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
19//! actual tasks.
Victor Hsieh272aa242021-02-01 14:19:20 -080020
Alan Stokes9e2c5d52021-07-21 11:29:10 +010021use anyhow::Result;
22use log::error;
Victor Hsieh272aa242021-02-01 14:19:20 -080023use minijail::{self, Minijail};
Victor Hsiehebb1d902021-08-06 13:00:18 -070024use std::ffi::CString;
25use std::os::unix::io::AsRawFd;
26use std::path::{Path, PathBuf};
Victor Hsieh272aa242021-02-01 14:19:20 -080027
Alan Stokes7ec4e7f2021-07-21 11:29:10 +010028use crate::signer::Signer;
Victor Hsiehebb1d902021-08-06 13:00:18 -070029use authfs_aidl_interface::aidl::com::android::virt::fs::{
30 AuthFsConfig::AuthFsConfig, IAuthFs::IAuthFs, IAuthFsService::IAuthFsService,
31 InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
32};
33use authfs_aidl_interface::binder::ParcelFileDescriptor;
Victor Hsieh272aa242021-02-01 14:19:20 -080034use compos_aidl_interface::aidl::com::android::compos::ICompService::{
35 BnCompService, ICompService,
36};
37use compos_aidl_interface::aidl::com::android::compos::Metadata::Metadata;
38use compos_aidl_interface::binder::{
Victor Hsiehebb1d902021-08-06 13:00:18 -070039 BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
Victor Hsieh272aa242021-02-01 14:19:20 -080040};
41
Victor Hsiehebb1d902021-08-06 13:00:18 -070042const AUTHFS_SERVICE_NAME: &str = "authfs_service";
Alan Stokes9e2c5d52021-07-21 11:29:10 +010043
Victor Hsiehebb1d902021-08-06 13:00:18 -070044/// The number that represents the file descriptor number expecting by the task. The number may be
45/// meaningless in the current process.
46pub type PseudoRawFd = i32;
Victor Hsieh272aa242021-02-01 14:19:20 -080047
Alan Stokes9e2c5d52021-07-21 11:29:10 +010048/// Constructs a binder object that implements ICompService. task_bin is the path to the binary that will
49/// be run when execute() is called. If debuggable is true then stdout/stderr from the binary will be
50/// available for debugging.
Alan Stokes7ec4e7f2021-07-21 11:29:10 +010051pub fn new_binder(
52 task_bin: String,
53 debuggable: bool,
54 signer: Option<Box<dyn Signer>>,
55) -> Strong<dyn ICompService> {
Victor Hsiehebb1d902021-08-06 13:00:18 -070056 let service = CompService { task_bin: PathBuf::from(task_bin), debuggable, signer };
Alan Stokes9e2c5d52021-07-21 11:29:10 +010057 BnCompService::new_binder(service, BinderFeatures::default())
58}
59
Victor Hsieh272aa242021-02-01 14:19:20 -080060struct CompService {
Victor Hsiehebb1d902021-08-06 13:00:18 -070061 task_bin: PathBuf,
Victor Hsieh272aa242021-02-01 14:19:20 -080062 debuggable: bool,
Alan Stokes7ec4e7f2021-07-21 11:29:10 +010063 #[allow(dead_code)] // TODO: Make use of this
64 signer: Option<Box<dyn Signer>>,
Victor Hsieh272aa242021-02-01 14:19:20 -080065}
66
Victor Hsieh272aa242021-02-01 14:19:20 -080067impl Interface for CompService {}
68
69impl ICompService for CompService {
70 fn execute(&self, args: &[String], metadata: &Metadata) -> BinderResult<i8> {
Victor Hsiehebb1d902021-08-06 13:00:18 -070071 // Mount authfs (via authfs_service).
72 let authfs_config = build_authfs_config(metadata);
73 let authfs = get_authfs_service()?.mount(&authfs_config)?;
Victor Hsieh272aa242021-02-01 14:19:20 -080074
Victor Hsiehebb1d902021-08-06 13:00:18 -070075 // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
76 // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
77 // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
78 // setup.
79 let fd_mapping =
80 open_authfs_files_for_fd_mapping(&authfs, &authfs_config).map_err(|e| {
81 new_binder_exception(
82 ExceptionCode::SERVICE_SPECIFIC,
83 format!("Failed to create FDs on authfs: {:?}", e),
84 )
85 })?;
86
87 let jail =
88 spawn_jailed_task(&self.task_bin, args, fd_mapping, self.debuggable).map_err(|e| {
89 new_binder_exception(
90 ExceptionCode::SERVICE_SPECIFIC,
91 format!("Failed to spawn the task: {:?}", e),
92 )
93 })?;
94 let jail_result = jail.wait();
95
96 // Be explicit about the lifetime, which should last at least until the task is finished.
97 drop(authfs);
98
99 match jail_result {
Victor Hsieh272aa242021-02-01 14:19:20 -0800100 Ok(_) => Ok(0), // TODO(b/161471326): Sign the output on succeed.
101 Err(minijail::Error::ReturnCode(exit_code)) => {
102 error!("Task failed with exit code {}", exit_code);
103 Err(Status::from(StatusCode::FAILED_TRANSACTION))
104 }
105 Err(e) => {
Victor Hsiehebb1d902021-08-06 13:00:18 -0700106 error!("Unexpected minijail error: {}", e);
Victor Hsieh272aa242021-02-01 14:19:20 -0800107 Err(Status::from(StatusCode::UNKNOWN_ERROR))
108 }
109 }
110 }
111}
Victor Hsiehebb1d902021-08-06 13:00:18 -0700112
113fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
114 Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
115}
116
117fn build_authfs_config(metadata: &Metadata) -> AuthFsConfig {
118 AuthFsConfig {
119 port: 3264, // TODO: support dynamic port
120 inputFdAnnotations: metadata
121 .input_fd_annotations
122 .iter()
123 .map(|x| InputFdAnnotation { fd: x.fd, fileSize: x.file_size })
124 .collect(),
125 outputFdAnnotations: metadata
126 .output_fd_annotations
127 .iter()
128 .map(|x| OutputFdAnnotation { fd: x.fd })
129 .collect(),
130 }
131}
132
133fn open_authfs_files_for_fd_mapping(
134 authfs: &Strong<dyn IAuthFs>,
135 config: &AuthFsConfig,
136) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
137 let mut fd_mapping = Vec::new();
138
139 let results: Result<Vec<_>> = config
140 .inputFdAnnotations
141 .iter()
142 .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
143 .collect();
144 fd_mapping.append(&mut results?);
145
146 let results: Result<Vec<_>> = config
147 .outputFdAnnotations
148 .iter()
149 .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
150 .collect();
151 fd_mapping.append(&mut results?);
152
153 Ok(fd_mapping)
154}
155
156fn spawn_jailed_task(
157 executable: &Path,
158 args: &[String],
159 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
160 debuggable: bool,
161) -> Result<Minijail> {
162 // TODO(b/185175567): Run in a more restricted sandbox.
163 let jail = Minijail::new()?;
164
165 let mut preserve_fds = if debuggable {
166 // Inherit/redirect stdout/stderr for debugging, assuming no conflict
167 vec![(1, 1), (2, 2)]
168 } else {
169 vec![]
170 };
171
172 preserve_fds.extend(fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)));
173
174 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
175 Ok(jail)
176}
177
178fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
179 Status::new_exception(exception, CString::new(message.as_ref()).as_deref().ok())
180}