blob: f8a66c26e140bd8d717fa346ca53bbfd2b2c5a89 [file] [log] [blame]
Victor Hsieh51789de2021-08-06 16:50:49 -07001/*
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
Victor Hsieh6e340382021-08-13 12:18:02 -070017use anyhow::{anyhow, bail, Context, Result};
Alan Stokes9646db92021-12-14 13:22:33 +000018use log::{debug, error, info};
Victor Hsieh51789de2021-08-06 16:50:49 -070019use minijail::{self, Minijail};
Victor Hsiehf9968692021-11-18 11:34:39 -080020use std::env;
Alan Stokes9646db92021-12-14 13:22:33 +000021use std::fs::File;
Victor Hsieh6e340382021-08-13 12:18:02 -070022use std::os::unix::io::{AsRawFd, RawFd};
Victor Hsiehf9968692021-11-18 11:34:39 -080023use std::path::{Path, PathBuf};
Victor Hsieh51789de2021-08-06 16:50:49 -070024
Victor Hsieh9ed27182021-08-25 15:52:42 -070025use crate::fsverity;
Victor Hsieh51789de2021-08-06 16:50:49 -070026use authfs_aidl_interface::aidl::com::android::virt::fs::{
Victor Hsieh015bcb52021-11-17 17:28:01 -080027 AuthFsConfig::{
Victor Hsiehf9968692021-11-18 11:34:39 -080028 AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
29 InputFdAnnotation::InputFdAnnotation, OutputDirFdAnnotation::OutputDirFdAnnotation,
30 OutputFdAnnotation::OutputFdAnnotation,
Victor Hsieh015bcb52021-11-17 17:28:01 -080031 },
32 IAuthFs::IAuthFs,
33 IAuthFsService::IAuthFsService,
Victor Hsieh51789de2021-08-06 16:50:49 -070034};
35use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Victor Hsieh13333e82021-09-03 15:17:32 -070036use compos_aidl_interface::aidl::com::android::compos::FdAnnotation::FdAnnotation;
Victor Hsieh51789de2021-08-06 16:50:49 -070037
Victor Hsiehf9968692021-11-18 11:34:39 -080038const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
39
Victor Hsieh51789de2021-08-06 16:50:49 -070040/// The number that represents the file descriptor number expecting by the task. The number may be
41/// meaningless in the current process.
42pub type PseudoRawFd = i32;
43
Victor Hsieh6e340382021-08-13 12:18:02 -070044pub enum CompilerOutput {
45 /// Fs-verity digests of output files, if the compiler finishes successfully.
Victor Hsieh9ed27182021-08-25 15:52:42 -070046 Digests {
47 oat: fsverity::Sha256Digest,
48 vdex: fsverity::Sha256Digest,
49 image: fsverity::Sha256Digest,
50 },
Victor Hsieh6e340382021-08-13 12:18:02 -070051 /// Exit code returned by the compiler, if not 0.
52 ExitCode(i8),
53}
54
55struct CompilerOutputParcelFds {
56 oat: ParcelFileDescriptor,
57 vdex: ParcelFileDescriptor,
58 image: ParcelFileDescriptor,
59}
60
Victor Hsiehf9968692021-11-18 11:34:39 -080061pub fn odrefresh(
62 odrefresh_path: &Path,
Alan Stokes9646db92021-12-14 13:22:33 +000063 target_dir_name: &str,
Victor Hsiehf9968692021-11-18 11:34:39 -080064 system_dir_fd: i32,
65 output_dir_fd: i32,
Alan Stokes9646db92021-12-14 13:22:33 +000066 staging_dir_fd: i32,
Victor Hsiehf9968692021-11-18 11:34:39 -080067 zygote_arch: &str,
68 authfs_service: Strong<dyn IAuthFsService>,
Alan Stokes9646db92021-12-14 13:22:33 +000069) -> Result<i8> {
Victor Hsiehf9968692021-11-18 11:34:39 -080070 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
71 // is out of scope.
72 let authfs_config = AuthFsConfig {
73 port: FD_SERVER_PORT,
74 inputDirFdAnnotations: vec![InputDirFdAnnotation {
75 fd: system_dir_fd,
76 // TODO(206869687): Replace /dev/null with the real path when possible.
77 manifestPath: "/dev/null".to_string(),
78 prefix: "/system".to_string(),
79 }],
Alan Stokes9646db92021-12-14 13:22:33 +000080 outputDirFdAnnotations: vec![
81 OutputDirFdAnnotation { fd: output_dir_fd },
82 OutputDirFdAnnotation { fd: staging_dir_fd },
83 ],
Victor Hsiehf9968692021-11-18 11:34:39 -080084 ..Default::default()
85 };
86 let authfs = authfs_service.mount(&authfs_config)?;
87 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
88
89 let mut android_root = mountpoint.clone();
90 android_root.push(system_dir_fd.to_string());
91 android_root.push("system");
92 env::set_var("ANDROID_ROOT", &android_root);
93
Victor Hsieh64df53d2021-11-30 17:09:51 -080094 let mut art_apex_data = mountpoint.clone();
95 art_apex_data.push(output_dir_fd.to_string());
96 env::set_var("ART_APEX_DATA", &art_apex_data);
97
Victor Hsiehf9968692021-11-18 11:34:39 -080098 let mut staging_dir = mountpoint;
Alan Stokes9646db92021-12-14 13:22:33 +000099 staging_dir.push(staging_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800100
101 let args = vec![
102 "odrefresh".to_string(),
103 format!("--zygote-arch={}", zygote_arch),
Alan Stokes9646db92021-12-14 13:22:33 +0000104 format!("--dalvik-cache={}", target_dir_name),
Victor Hsieh64df53d2021-11-30 17:09:51 -0800105 "--no-refresh".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800106 format!("--staging-dir={}", staging_dir.display()),
107 "--force-compile".to_string(),
108 ];
Alan Stokes9646db92021-12-14 13:22:33 +0000109 debug!("Running odrefresh with args: {:?}", &args);
Victor Hsiehf9968692021-11-18 11:34:39 -0800110 let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
111 .context("Spawn odrefresh")?;
112 match jail.wait() {
113 // TODO(161471326): On success, sign all files in the output directory.
Alan Stokes9646db92021-12-14 13:22:33 +0000114 Ok(()) => Ok(0i8),
Victor Hsiehf9968692021-11-18 11:34:39 -0800115 Err(minijail::Error::ReturnCode(exit_code)) => {
Alan Stokes9646db92021-12-14 13:22:33 +0000116 info!("odrefresh exited with exit code {}", exit_code);
117 Ok(exit_code as i8)
Victor Hsiehf9968692021-11-18 11:34:39 -0800118 }
119 Err(e) => {
120 bail!("Unexpected minijail error: {}", e)
121 }
122 }
123}
124
Victor Hsieh13333e82021-09-03 15:17:32 -0700125/// Runs the compiler with given flags with file descriptors described in `fd_annotation` retrieved
126/// via `authfs_service`. Returns exit code of the compiler process.
Victor Hsieh3c044c42021-10-01 17:17:10 -0700127pub fn compile_cmd(
Victor Hsieh51789de2021-08-06 16:50:49 -0700128 compiler_path: &Path,
129 compiler_args: &[String],
130 authfs_service: Strong<dyn IAuthFsService>,
Victor Hsieh13333e82021-09-03 15:17:32 -0700131 fd_annotation: &FdAnnotation,
Victor Hsieh6e340382021-08-13 12:18:02 -0700132) -> Result<CompilerOutput> {
133 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
134 // is out of scope.
Victor Hsieh13333e82021-09-03 15:17:32 -0700135 let authfs_config = build_authfs_config(fd_annotation);
Victor Hsieh51789de2021-08-06 16:50:49 -0700136 let authfs = authfs_service.mount(&authfs_config)?;
137
138 // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
139 // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
140 // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
141 // setup.
142 let fd_mapping =
143 open_authfs_files_for_fd_mapping(&authfs, &authfs_config).context("Open on authfs")?;
144
145 let jail =
146 spawn_jailed_task(compiler_path, compiler_args, fd_mapping).context("Spawn dex2oat")?;
147 let jail_result = jail.wait();
148
Victor Hsieh6e340382021-08-13 12:18:02 -0700149 let parcel_fds = parse_compiler_args(&authfs, compiler_args)?;
150 let oat_file: &File = parcel_fds.oat.as_ref();
151 let vdex_file: &File = parcel_fds.vdex.as_ref();
152 let image_file: &File = parcel_fds.image.as_ref();
Victor Hsieh51789de2021-08-06 16:50:49 -0700153
154 match jail_result {
Victor Hsieh6e340382021-08-13 12:18:02 -0700155 Ok(()) => Ok(CompilerOutput::Digests {
Victor Hsieh9ed27182021-08-25 15:52:42 -0700156 oat: fsverity::measure(oat_file.as_raw_fd())?,
157 vdex: fsverity::measure(vdex_file.as_raw_fd())?,
158 image: fsverity::measure(image_file.as_raw_fd())?,
Victor Hsieh6e340382021-08-13 12:18:02 -0700159 }),
Victor Hsieh51789de2021-08-06 16:50:49 -0700160 Err(minijail::Error::ReturnCode(exit_code)) => {
Victor Hsieh6e340382021-08-13 12:18:02 -0700161 error!("dex2oat failed with exit code {}", exit_code);
162 Ok(CompilerOutput::ExitCode(exit_code as i8))
Victor Hsieh51789de2021-08-06 16:50:49 -0700163 }
164 Err(e) => {
165 bail!("Unexpected minijail error: {}", e)
166 }
167 }
168}
169
Victor Hsieh6e340382021-08-13 12:18:02 -0700170fn parse_compiler_args(
171 authfs: &Strong<dyn IAuthFs>,
172 args: &[String],
173) -> Result<CompilerOutputParcelFds> {
174 const OAT_FD_PREFIX: &str = "--oat-fd=";
175 const VDEX_FD_PREFIX: &str = "--output-vdex-fd=";
176 const IMAGE_FD_PREFIX: &str = "--image-fd=";
177 const APP_IMAGE_FD_PREFIX: &str = "--app-image-fd=";
178
179 let mut oat = None;
180 let mut vdex = None;
181 let mut image = None;
182
183 for arg in args {
184 if let Some(value) = arg.strip_prefix(OAT_FD_PREFIX) {
185 let fd = value.parse::<RawFd>().context("Invalid --oat-fd flag")?;
186 debug_assert!(oat.is_none());
187 oat = Some(authfs.openFile(fd, false)?);
188 } else if let Some(value) = arg.strip_prefix(VDEX_FD_PREFIX) {
189 let fd = value.parse::<RawFd>().context("Invalid --output-vdex-fd flag")?;
190 debug_assert!(vdex.is_none());
191 vdex = Some(authfs.openFile(fd, false)?);
192 } else if let Some(value) = arg.strip_prefix(IMAGE_FD_PREFIX) {
193 let fd = value.parse::<RawFd>().context("Invalid --image-fd flag")?;
194 debug_assert!(image.is_none());
195 image = Some(authfs.openFile(fd, false)?);
196 } else if let Some(value) = arg.strip_prefix(APP_IMAGE_FD_PREFIX) {
197 let fd = value.parse::<RawFd>().context("Invalid --app-image-fd flag")?;
198 debug_assert!(image.is_none());
199 image = Some(authfs.openFile(fd, false)?);
200 }
201 }
202
203 Ok(CompilerOutputParcelFds {
204 oat: oat.ok_or_else(|| anyhow!("Missing --oat-fd"))?,
205 vdex: vdex.ok_or_else(|| anyhow!("Missing --vdex-fd"))?,
206 image: image.ok_or_else(|| anyhow!("Missing --image-fd or --app-image-fd"))?,
207 })
208}
209
Victor Hsieh13333e82021-09-03 15:17:32 -0700210fn build_authfs_config(fd_annotation: &FdAnnotation) -> AuthFsConfig {
Victor Hsieh51789de2021-08-06 16:50:49 -0700211 AuthFsConfig {
Victor Hsiehf9968692021-11-18 11:34:39 -0800212 port: FD_SERVER_PORT,
Victor Hsieh13333e82021-09-03 15:17:32 -0700213 inputFdAnnotations: fd_annotation
214 .input_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700215 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700216 .map(|fd| InputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700217 .collect(),
Victor Hsieh13333e82021-09-03 15:17:32 -0700218 outputFdAnnotations: fd_annotation
219 .output_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700220 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700221 .map(|fd| OutputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700222 .collect(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800223 ..Default::default()
Victor Hsieh51789de2021-08-06 16:50:49 -0700224 }
225}
226
227fn open_authfs_files_for_fd_mapping(
228 authfs: &Strong<dyn IAuthFs>,
229 config: &AuthFsConfig,
230) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
231 let mut fd_mapping = Vec::new();
232
233 let results: Result<Vec<_>> = config
234 .inputFdAnnotations
235 .iter()
236 .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
237 .collect();
238 fd_mapping.append(&mut results?);
239
240 let results: Result<Vec<_>> = config
241 .outputFdAnnotations
242 .iter()
243 .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
244 .collect();
245 fd_mapping.append(&mut results?);
246
247 Ok(fd_mapping)
248}
249
250fn spawn_jailed_task(
251 executable: &Path,
252 args: &[String],
253 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
254) -> Result<Minijail> {
255 // TODO(b/185175567): Run in a more restricted sandbox.
256 let jail = Minijail::new()?;
257 let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
258 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
259 Ok(jail)
260}