blob: 2ca4dd40a6bd30354985b6aa363769ffa44aebad [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 Stokes46a1dff2021-12-14 10:56:05 +000021use std::fs::{read_dir, File};
Victor Hsieh6e340382021-08-13 12:18:02 -070022use std::os::unix::io::{AsRawFd, RawFd};
Alan Stokes35bac3c2021-12-16 14:37:24 +000023use std::path::{self, Path, PathBuf};
Victor Hsieh51789de2021-08-06 16:50:49 -070024
Alan Stokes46a1dff2021-12-14 10:56:05 +000025use crate::artifact_signer::ArtifactSigner;
26use crate::compos_key_service::Signer;
Victor Hsieh9ed27182021-08-25 15:52:42 -070027use crate::fsverity;
Victor Hsieh51789de2021-08-06 16:50:49 -070028use authfs_aidl_interface::aidl::com::android::virt::fs::{
Victor Hsieh015bcb52021-11-17 17:28:01 -080029 AuthFsConfig::{
Victor Hsiehf9968692021-11-18 11:34:39 -080030 AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
31 InputFdAnnotation::InputFdAnnotation, OutputDirFdAnnotation::OutputDirFdAnnotation,
32 OutputFdAnnotation::OutputFdAnnotation,
Victor Hsieh015bcb52021-11-17 17:28:01 -080033 },
34 IAuthFs::IAuthFs,
35 IAuthFsService::IAuthFsService,
Victor Hsieh51789de2021-08-06 16:50:49 -070036};
37use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Victor Hsieh13333e82021-09-03 15:17:32 -070038use compos_aidl_interface::aidl::com::android::compos::FdAnnotation::FdAnnotation;
Alan Stokes46a1dff2021-12-14 10:56:05 +000039use compos_common::odrefresh::ExitCode;
Victor Hsieh51789de2021-08-06 16:50:49 -070040
Victor Hsiehf9968692021-11-18 11:34:39 -080041const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
42
Victor Hsieh51789de2021-08-06 16:50:49 -070043/// The number that represents the file descriptor number expecting by the task. The number may be
44/// meaningless in the current process.
45pub type PseudoRawFd = i32;
46
Victor Hsieh6e340382021-08-13 12:18:02 -070047pub enum CompilerOutput {
48 /// Fs-verity digests of output files, if the compiler finishes successfully.
Victor Hsieh9ed27182021-08-25 15:52:42 -070049 Digests {
50 oat: fsverity::Sha256Digest,
51 vdex: fsverity::Sha256Digest,
52 image: fsverity::Sha256Digest,
53 },
Victor Hsieh6e340382021-08-13 12:18:02 -070054 /// Exit code returned by the compiler, if not 0.
55 ExitCode(i8),
56}
57
58struct CompilerOutputParcelFds {
59 oat: ParcelFileDescriptor,
60 vdex: ParcelFileDescriptor,
61 image: ParcelFileDescriptor,
62}
63
Alan Stokes46a1dff2021-12-14 10:56:05 +000064pub struct OdrefreshContext<'a> {
Victor Hsiehf9968692021-11-18 11:34:39 -080065 system_dir_fd: i32,
66 output_dir_fd: i32,
Alan Stokes9646db92021-12-14 13:22:33 +000067 staging_dir_fd: i32,
Alan Stokes46a1dff2021-12-14 10:56:05 +000068 target_dir_name: &'a str,
69 zygote_arch: &'a str,
70}
71
72impl<'a> OdrefreshContext<'a> {
73 pub fn new(
74 system_dir_fd: i32,
75 output_dir_fd: i32,
76 staging_dir_fd: i32,
77 target_dir_name: &'a str,
78 zygote_arch: &'a str,
79 ) -> Result<Self> {
80 if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
81 bail!("The remote FDs are expected to be non-negative");
82 }
83 if zygote_arch != "zygote64" && zygote_arch != "zygote64_32" {
84 bail!("Invalid zygote arch");
85 }
Alan Stokes35bac3c2021-12-16 14:37:24 +000086 // Disallow any sort of path traversal
87 if target_dir_name.contains(path::MAIN_SEPARATOR) {
88 bail!("Invalid target directory {}", target_dir_name);
89 }
90
Alan Stokes46a1dff2021-12-14 10:56:05 +000091 Ok(Self { system_dir_fd, output_dir_fd, staging_dir_fd, target_dir_name, zygote_arch })
92 }
93}
94
95pub fn odrefresh(
96 odrefresh_path: &Path,
97 context: OdrefreshContext,
Victor Hsiehf9968692021-11-18 11:34:39 -080098 authfs_service: Strong<dyn IAuthFsService>,
Alan Stokes46a1dff2021-12-14 10:56:05 +000099 signer: Signer,
100) -> Result<ExitCode> {
Victor Hsiehf9968692021-11-18 11:34:39 -0800101 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
102 // is out of scope.
103 let authfs_config = AuthFsConfig {
104 port: FD_SERVER_PORT,
105 inputDirFdAnnotations: vec![InputDirFdAnnotation {
Alan Stokes46a1dff2021-12-14 10:56:05 +0000106 fd: context.system_dir_fd,
Victor Hsiehf9968692021-11-18 11:34:39 -0800107 // TODO(206869687): Replace /dev/null with the real path when possible.
108 manifestPath: "/dev/null".to_string(),
109 prefix: "/system".to_string(),
110 }],
Alan Stokes9646db92021-12-14 13:22:33 +0000111 outputDirFdAnnotations: vec![
Alan Stokes46a1dff2021-12-14 10:56:05 +0000112 OutputDirFdAnnotation { fd: context.output_dir_fd },
113 OutputDirFdAnnotation { fd: context.staging_dir_fd },
Alan Stokes9646db92021-12-14 13:22:33 +0000114 ],
Victor Hsiehf9968692021-11-18 11:34:39 -0800115 ..Default::default()
116 };
117 let authfs = authfs_service.mount(&authfs_config)?;
118 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
119
120 let mut android_root = mountpoint.clone();
Alan Stokes46a1dff2021-12-14 10:56:05 +0000121 android_root.push(context.system_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800122 android_root.push("system");
123 env::set_var("ANDROID_ROOT", &android_root);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000124 debug!("ANDROID_ROOT={:?}", &android_root);
Victor Hsiehf9968692021-11-18 11:34:39 -0800125
Alan Stokes46a1dff2021-12-14 10:56:05 +0000126 let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
Victor Hsieh64df53d2021-11-30 17:09:51 -0800127 env::set_var("ART_APEX_DATA", &art_apex_data);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000128 debug!("ART_APEX_DATA={:?}", &art_apex_data);
Victor Hsieh64df53d2021-11-30 17:09:51 -0800129
Alan Stokes46a1dff2021-12-14 10:56:05 +0000130 let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800131
132 let args = vec![
133 "odrefresh".to_string(),
Alan Stokes46a1dff2021-12-14 10:56:05 +0000134 format!("--zygote-arch={}", context.zygote_arch),
135 format!("--dalvik-cache={}", context.target_dir_name),
Victor Hsieh64df53d2021-11-30 17:09:51 -0800136 "--no-refresh".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800137 format!("--staging-dir={}", staging_dir.display()),
138 "--force-compile".to_string(),
139 ];
Alan Stokes9646db92021-12-14 13:22:33 +0000140 debug!("Running odrefresh with args: {:?}", &args);
Victor Hsiehf9968692021-11-18 11:34:39 -0800141 let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
142 .context("Spawn odrefresh")?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000143 let exit_code = match jail.wait() {
144 Ok(_) => Result::<u8>::Ok(0),
145 Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
Victor Hsiehf9968692021-11-18 11:34:39 -0800146 Err(e) => {
147 bail!("Unexpected minijail error: {}", e)
148 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000149 }?;
150
Alan Stokes126fd512021-12-16 15:00:01 +0000151 let exit_code = ExitCode::from_i32(exit_code.into())?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000152 info!("odrefresh exited with {:?}", exit_code);
153
154 if exit_code == ExitCode::CompilationSuccess {
155 // authfs only shows us the files we created, so it's ok to just sign everything under
156 // the target directory.
157 let target_dir = art_apex_data.join(context.target_dir_name);
158 let mut artifact_signer = ArtifactSigner::new(&target_dir);
159 add_artifacts(&target_dir, &mut artifact_signer)?;
160
161 artifact_signer.write_info_and_signature(signer, &target_dir.join("compos.info"))?;
Victor Hsiehf9968692021-11-18 11:34:39 -0800162 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000163
164 Ok(exit_code)
165}
166
167fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
168 for entry in
169 read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
170 {
171 let entry = entry?;
172 let file_type = entry.file_type()?;
173 if file_type.is_dir() {
174 add_artifacts(&entry.path(), artifact_signer)?;
175 } else if file_type.is_file() {
176 artifact_signer.add_artifact(&entry.path())?;
177 } else {
178 // authfs shouldn't create anything else, but just in case
179 bail!("Unexpected file type in artifacts: {:?}", entry);
180 }
181 }
182 Ok(())
Victor Hsiehf9968692021-11-18 11:34:39 -0800183}
184
Victor Hsieh13333e82021-09-03 15:17:32 -0700185/// Runs the compiler with given flags with file descriptors described in `fd_annotation` retrieved
186/// via `authfs_service`. Returns exit code of the compiler process.
Victor Hsieh3c044c42021-10-01 17:17:10 -0700187pub fn compile_cmd(
Victor Hsieh51789de2021-08-06 16:50:49 -0700188 compiler_path: &Path,
189 compiler_args: &[String],
190 authfs_service: Strong<dyn IAuthFsService>,
Victor Hsieh13333e82021-09-03 15:17:32 -0700191 fd_annotation: &FdAnnotation,
Victor Hsieh6e340382021-08-13 12:18:02 -0700192) -> Result<CompilerOutput> {
193 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
194 // is out of scope.
Victor Hsieh13333e82021-09-03 15:17:32 -0700195 let authfs_config = build_authfs_config(fd_annotation);
Victor Hsieh51789de2021-08-06 16:50:49 -0700196 let authfs = authfs_service.mount(&authfs_config)?;
197
198 // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
199 // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
200 // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
201 // setup.
202 let fd_mapping =
203 open_authfs_files_for_fd_mapping(&authfs, &authfs_config).context("Open on authfs")?;
204
205 let jail =
206 spawn_jailed_task(compiler_path, compiler_args, fd_mapping).context("Spawn dex2oat")?;
207 let jail_result = jail.wait();
208
Victor Hsieh6e340382021-08-13 12:18:02 -0700209 let parcel_fds = parse_compiler_args(&authfs, compiler_args)?;
210 let oat_file: &File = parcel_fds.oat.as_ref();
211 let vdex_file: &File = parcel_fds.vdex.as_ref();
212 let image_file: &File = parcel_fds.image.as_ref();
Victor Hsieh51789de2021-08-06 16:50:49 -0700213
214 match jail_result {
Victor Hsieh6e340382021-08-13 12:18:02 -0700215 Ok(()) => Ok(CompilerOutput::Digests {
Victor Hsieh9ed27182021-08-25 15:52:42 -0700216 oat: fsverity::measure(oat_file.as_raw_fd())?,
217 vdex: fsverity::measure(vdex_file.as_raw_fd())?,
218 image: fsverity::measure(image_file.as_raw_fd())?,
Victor Hsieh6e340382021-08-13 12:18:02 -0700219 }),
Victor Hsieh51789de2021-08-06 16:50:49 -0700220 Err(minijail::Error::ReturnCode(exit_code)) => {
Victor Hsieh6e340382021-08-13 12:18:02 -0700221 error!("dex2oat failed with exit code {}", exit_code);
222 Ok(CompilerOutput::ExitCode(exit_code as i8))
Victor Hsieh51789de2021-08-06 16:50:49 -0700223 }
224 Err(e) => {
225 bail!("Unexpected minijail error: {}", e)
226 }
227 }
228}
229
Victor Hsieh6e340382021-08-13 12:18:02 -0700230fn parse_compiler_args(
231 authfs: &Strong<dyn IAuthFs>,
232 args: &[String],
233) -> Result<CompilerOutputParcelFds> {
234 const OAT_FD_PREFIX: &str = "--oat-fd=";
235 const VDEX_FD_PREFIX: &str = "--output-vdex-fd=";
236 const IMAGE_FD_PREFIX: &str = "--image-fd=";
237 const APP_IMAGE_FD_PREFIX: &str = "--app-image-fd=";
238
239 let mut oat = None;
240 let mut vdex = None;
241 let mut image = None;
242
243 for arg in args {
244 if let Some(value) = arg.strip_prefix(OAT_FD_PREFIX) {
245 let fd = value.parse::<RawFd>().context("Invalid --oat-fd flag")?;
246 debug_assert!(oat.is_none());
247 oat = Some(authfs.openFile(fd, false)?);
248 } else if let Some(value) = arg.strip_prefix(VDEX_FD_PREFIX) {
249 let fd = value.parse::<RawFd>().context("Invalid --output-vdex-fd flag")?;
250 debug_assert!(vdex.is_none());
251 vdex = Some(authfs.openFile(fd, false)?);
252 } else if let Some(value) = arg.strip_prefix(IMAGE_FD_PREFIX) {
253 let fd = value.parse::<RawFd>().context("Invalid --image-fd flag")?;
254 debug_assert!(image.is_none());
255 image = Some(authfs.openFile(fd, false)?);
256 } else if let Some(value) = arg.strip_prefix(APP_IMAGE_FD_PREFIX) {
257 let fd = value.parse::<RawFd>().context("Invalid --app-image-fd flag")?;
258 debug_assert!(image.is_none());
259 image = Some(authfs.openFile(fd, false)?);
260 }
261 }
262
263 Ok(CompilerOutputParcelFds {
264 oat: oat.ok_or_else(|| anyhow!("Missing --oat-fd"))?,
265 vdex: vdex.ok_or_else(|| anyhow!("Missing --vdex-fd"))?,
266 image: image.ok_or_else(|| anyhow!("Missing --image-fd or --app-image-fd"))?,
267 })
268}
269
Victor Hsieh13333e82021-09-03 15:17:32 -0700270fn build_authfs_config(fd_annotation: &FdAnnotation) -> AuthFsConfig {
Victor Hsieh51789de2021-08-06 16:50:49 -0700271 AuthFsConfig {
Victor Hsiehf9968692021-11-18 11:34:39 -0800272 port: FD_SERVER_PORT,
Victor Hsieh13333e82021-09-03 15:17:32 -0700273 inputFdAnnotations: fd_annotation
274 .input_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700275 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700276 .map(|fd| InputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700277 .collect(),
Victor Hsieh13333e82021-09-03 15:17:32 -0700278 outputFdAnnotations: fd_annotation
279 .output_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700280 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700281 .map(|fd| OutputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700282 .collect(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800283 ..Default::default()
Victor Hsieh51789de2021-08-06 16:50:49 -0700284 }
285}
286
287fn open_authfs_files_for_fd_mapping(
288 authfs: &Strong<dyn IAuthFs>,
289 config: &AuthFsConfig,
290) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
291 let mut fd_mapping = Vec::new();
292
293 let results: Result<Vec<_>> = config
294 .inputFdAnnotations
295 .iter()
296 .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
297 .collect();
298 fd_mapping.append(&mut results?);
299
300 let results: Result<Vec<_>> = config
301 .outputFdAnnotations
302 .iter()
303 .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
304 .collect();
305 fd_mapping.append(&mut results?);
306
307 Ok(fd_mapping)
308}
309
310fn spawn_jailed_task(
311 executable: &Path,
312 args: &[String],
313 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
314) -> Result<Minijail> {
315 // TODO(b/185175567): Run in a more restricted sandbox.
316 let jail = Minijail::new()?;
317 let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
318 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
319 Ok(jail)
320}