blob: 48370292fa327089e11263e6b7035bb26e1e8cf1 [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 Stokes92472512022-01-04 11:48:38 +000018use log::{debug, error, info, warn};
Victor Hsieh51789de2021-08-06 16:50:49 -070019use minijail::{self, Minijail};
Alan Stokes92472512022-01-04 11:48:38 +000020use regex::Regex;
Victor Hsiehf9968692021-11-18 11:34:39 -080021use std::env;
Alan Stokes92472512022-01-04 11:48:38 +000022use std::ffi::OsString;
Alan Stokes46a1dff2021-12-14 10:56:05 +000023use std::fs::{read_dir, File};
Victor Hsieh6e340382021-08-13 12:18:02 -070024use std::os::unix::io::{AsRawFd, RawFd};
Alan Stokes35bac3c2021-12-16 14:37:24 +000025use std::path::{self, Path, PathBuf};
Alan Stokes92472512022-01-04 11:48:38 +000026use std::process::Command;
Victor Hsieh51789de2021-08-06 16:50:49 -070027
Alan Stokes46a1dff2021-12-14 10:56:05 +000028use crate::artifact_signer::ArtifactSigner;
29use crate::compos_key_service::Signer;
Victor Hsieh9ed27182021-08-25 15:52:42 -070030use crate::fsverity;
Victor Hsieh51789de2021-08-06 16:50:49 -070031use authfs_aidl_interface::aidl::com::android::virt::fs::{
Victor Hsieh015bcb52021-11-17 17:28:01 -080032 AuthFsConfig::{
Victor Hsiehf9968692021-11-18 11:34:39 -080033 AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
34 InputFdAnnotation::InputFdAnnotation, OutputDirFdAnnotation::OutputDirFdAnnotation,
35 OutputFdAnnotation::OutputFdAnnotation,
Victor Hsieh015bcb52021-11-17 17:28:01 -080036 },
37 IAuthFs::IAuthFs,
38 IAuthFsService::IAuthFsService,
Victor Hsieh51789de2021-08-06 16:50:49 -070039};
40use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Victor Hsieh13333e82021-09-03 15:17:32 -070041use compos_aidl_interface::aidl::com::android::compos::FdAnnotation::FdAnnotation;
Alan Stokes46a1dff2021-12-14 10:56:05 +000042use compos_common::odrefresh::ExitCode;
Victor Hsieh51789de2021-08-06 16:50:49 -070043
Victor Hsiehf9968692021-11-18 11:34:39 -080044const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
45
Victor Hsieh51789de2021-08-06 16:50:49 -070046/// The number that represents the file descriptor number expecting by the task. The number may be
47/// meaningless in the current process.
48pub type PseudoRawFd = i32;
49
Victor Hsieh6e340382021-08-13 12:18:02 -070050pub enum CompilerOutput {
51 /// Fs-verity digests of output files, if the compiler finishes successfully.
Victor Hsieh9ed27182021-08-25 15:52:42 -070052 Digests {
53 oat: fsverity::Sha256Digest,
54 vdex: fsverity::Sha256Digest,
55 image: fsverity::Sha256Digest,
56 },
Victor Hsieh6e340382021-08-13 12:18:02 -070057 /// Exit code returned by the compiler, if not 0.
58 ExitCode(i8),
59}
60
61struct CompilerOutputParcelFds {
62 oat: ParcelFileDescriptor,
63 vdex: ParcelFileDescriptor,
64 image: ParcelFileDescriptor,
65}
66
Alan Stokes46a1dff2021-12-14 10:56:05 +000067pub struct OdrefreshContext<'a> {
Victor Hsiehf9968692021-11-18 11:34:39 -080068 system_dir_fd: i32,
69 output_dir_fd: i32,
Alan Stokes9646db92021-12-14 13:22:33 +000070 staging_dir_fd: i32,
Alan Stokes46a1dff2021-12-14 10:56:05 +000071 target_dir_name: &'a str,
72 zygote_arch: &'a str,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080073 system_server_compiler_filter: &'a str,
Alan Stokes46a1dff2021-12-14 10:56:05 +000074}
75
76impl<'a> OdrefreshContext<'a> {
77 pub fn new(
78 system_dir_fd: i32,
79 output_dir_fd: i32,
80 staging_dir_fd: i32,
81 target_dir_name: &'a str,
82 zygote_arch: &'a str,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080083 system_server_compiler_filter: &'a str,
Alan Stokes46a1dff2021-12-14 10:56:05 +000084 ) -> Result<Self> {
85 if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
86 bail!("The remote FDs are expected to be non-negative");
87 }
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080088 if !matches!(zygote_arch, "zygote64" | "zygote64_32") {
Alan Stokes46a1dff2021-12-14 10:56:05 +000089 bail!("Invalid zygote arch");
90 }
Alan Stokes35bac3c2021-12-16 14:37:24 +000091 // Disallow any sort of path traversal
92 if target_dir_name.contains(path::MAIN_SEPARATOR) {
93 bail!("Invalid target directory {}", target_dir_name);
94 }
95
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080096 // We're not validating/allowlisting the compiler filter, and just assume the compiler will
97 // reject an invalid string. We need to accept "verify" filter anyway, and potential
98 // performance degration by the attacker is not currently in scope. This also allows ART to
99 // specify new compiler filter and configure through system property without change to
100 // CompOS.
101
102 Ok(Self {
103 system_dir_fd,
104 output_dir_fd,
105 staging_dir_fd,
106 target_dir_name,
107 zygote_arch,
108 system_server_compiler_filter,
109 })
Alan Stokes46a1dff2021-12-14 10:56:05 +0000110 }
111}
112
113pub fn odrefresh(
114 odrefresh_path: &Path,
115 context: OdrefreshContext,
Victor Hsiehf9968692021-11-18 11:34:39 -0800116 authfs_service: Strong<dyn IAuthFsService>,
Alan Stokes46a1dff2021-12-14 10:56:05 +0000117 signer: Signer,
118) -> Result<ExitCode> {
Victor Hsiehf9968692021-11-18 11:34:39 -0800119 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
120 // is out of scope.
121 let authfs_config = AuthFsConfig {
122 port: FD_SERVER_PORT,
123 inputDirFdAnnotations: vec![InputDirFdAnnotation {
Alan Stokes46a1dff2021-12-14 10:56:05 +0000124 fd: context.system_dir_fd,
Victor Hsiehf9968692021-11-18 11:34:39 -0800125 // TODO(206869687): Replace /dev/null with the real path when possible.
126 manifestPath: "/dev/null".to_string(),
127 prefix: "/system".to_string(),
128 }],
Alan Stokes9646db92021-12-14 13:22:33 +0000129 outputDirFdAnnotations: vec![
Alan Stokes46a1dff2021-12-14 10:56:05 +0000130 OutputDirFdAnnotation { fd: context.output_dir_fd },
131 OutputDirFdAnnotation { fd: context.staging_dir_fd },
Alan Stokes9646db92021-12-14 13:22:33 +0000132 ],
Victor Hsiehf9968692021-11-18 11:34:39 -0800133 ..Default::default()
134 };
135 let authfs = authfs_service.mount(&authfs_config)?;
136 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
137
138 let mut android_root = mountpoint.clone();
Alan Stokes46a1dff2021-12-14 10:56:05 +0000139 android_root.push(context.system_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800140 android_root.push("system");
141 env::set_var("ANDROID_ROOT", &android_root);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000142 debug!("ANDROID_ROOT={:?}", &android_root);
Victor Hsiehf9968692021-11-18 11:34:39 -0800143
Alan Stokes46a1dff2021-12-14 10:56:05 +0000144 let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
Victor Hsieh64df53d2021-11-30 17:09:51 -0800145 env::set_var("ART_APEX_DATA", &art_apex_data);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000146 debug!("ART_APEX_DATA={:?}", &art_apex_data);
Victor Hsieh64df53d2021-11-30 17:09:51 -0800147
Alan Stokes46a1dff2021-12-14 10:56:05 +0000148 let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800149
Alan Stokes92472512022-01-04 11:48:38 +0000150 set_classpaths(&android_root)?;
151
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800152 let mut args = vec![
Victor Hsiehf9968692021-11-18 11:34:39 -0800153 "odrefresh".to_string(),
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000154 "--compilation-os-mode".to_string(),
Alan Stokes46a1dff2021-12-14 10:56:05 +0000155 format!("--zygote-arch={}", context.zygote_arch),
156 format!("--dalvik-cache={}", context.target_dir_name),
Victor Hsiehf9968692021-11-18 11:34:39 -0800157 format!("--staging-dir={}", staging_dir.display()),
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800158 "--no-refresh".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800159 ];
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800160
161 if !context.system_server_compiler_filter.is_empty() {
162 args.push(format!(
163 "--system-server-compiler-filter={}",
164 context.system_server_compiler_filter
165 ));
166 }
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000167
168 args.push("--compile".to_string());
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800169
Alan Stokes9646db92021-12-14 13:22:33 +0000170 debug!("Running odrefresh with args: {:?}", &args);
Victor Hsiehf9968692021-11-18 11:34:39 -0800171 let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
172 .context("Spawn odrefresh")?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000173 let exit_code = match jail.wait() {
174 Ok(_) => Result::<u8>::Ok(0),
175 Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
Victor Hsiehf9968692021-11-18 11:34:39 -0800176 Err(e) => {
177 bail!("Unexpected minijail error: {}", e)
178 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000179 }?;
180
Alan Stokes126fd512021-12-16 15:00:01 +0000181 let exit_code = ExitCode::from_i32(exit_code.into())?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000182 info!("odrefresh exited with {:?}", exit_code);
183
184 if exit_code == ExitCode::CompilationSuccess {
185 // authfs only shows us the files we created, so it's ok to just sign everything under
186 // the target directory.
187 let target_dir = art_apex_data.join(context.target_dir_name);
188 let mut artifact_signer = ArtifactSigner::new(&target_dir);
189 add_artifacts(&target_dir, &mut artifact_signer)?;
190
191 artifact_signer.write_info_and_signature(signer, &target_dir.join("compos.info"))?;
Victor Hsiehf9968692021-11-18 11:34:39 -0800192 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000193
194 Ok(exit_code)
195}
196
Alan Stokes92472512022-01-04 11:48:38 +0000197fn set_classpaths(android_root: &Path) -> Result<()> {
198 let export_lines = run_derive_classpath(android_root)?;
199 load_classpath_vars(&export_lines)
200}
201
202fn run_derive_classpath(android_root: &Path) -> Result<String> {
203 let classpaths_root = android_root.join("etc/classpaths");
204
205 let mut bootclasspath_arg = OsString::new();
206 bootclasspath_arg.push("--bootclasspath-fragment=");
207 bootclasspath_arg.push(classpaths_root.join("bootclasspath.pb"));
208
209 let mut systemserverclasspath_arg = OsString::new();
210 systemserverclasspath_arg.push("--systemserverclasspath-fragment=");
211 systemserverclasspath_arg.push(classpaths_root.join("systemserverclasspath.pb"));
212
213 let result = Command::new("/apex/com.android.sdkext/bin/derive_classpath")
214 .arg(bootclasspath_arg)
215 .arg(systemserverclasspath_arg)
216 .arg("/proc/self/fd/1")
217 .output()
218 .context("Failed to run derive_classpath")?;
219
220 if !result.status.success() {
221 bail!("derive_classpath returned {}", result.status);
222 }
223
224 String::from_utf8(result.stdout).context("Converting derive_classpath output")
225}
226
227fn load_classpath_vars(export_lines: &str) -> Result<()> {
228 // Each line should be in the format "export <var name> <value>"
229 let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
230 for line in export_lines.lines() {
231 if let Some(captures) = pattern.captures(line) {
232 let name = &captures[1];
233 let value = &captures[2];
234 // TODO(b/213416778) Don't modify our env, construct a fresh one for odrefresh
235 env::set_var(name, value);
236 } else {
237 warn!("Malformed line from derive_classpath: {}", line);
238 }
239 }
240
241 Ok(())
242}
243
Alan Stokes46a1dff2021-12-14 10:56:05 +0000244fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
245 for entry in
246 read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
247 {
248 let entry = entry?;
249 let file_type = entry.file_type()?;
250 if file_type.is_dir() {
251 add_artifacts(&entry.path(), artifact_signer)?;
252 } else if file_type.is_file() {
253 artifact_signer.add_artifact(&entry.path())?;
254 } else {
255 // authfs shouldn't create anything else, but just in case
256 bail!("Unexpected file type in artifacts: {:?}", entry);
257 }
258 }
259 Ok(())
Victor Hsiehf9968692021-11-18 11:34:39 -0800260}
261
Victor Hsieh13333e82021-09-03 15:17:32 -0700262/// Runs the compiler with given flags with file descriptors described in `fd_annotation` retrieved
263/// via `authfs_service`. Returns exit code of the compiler process.
Victor Hsieh3c044c42021-10-01 17:17:10 -0700264pub fn compile_cmd(
Victor Hsieh51789de2021-08-06 16:50:49 -0700265 compiler_path: &Path,
266 compiler_args: &[String],
267 authfs_service: Strong<dyn IAuthFsService>,
Victor Hsieh13333e82021-09-03 15:17:32 -0700268 fd_annotation: &FdAnnotation,
Victor Hsieh6e340382021-08-13 12:18:02 -0700269) -> Result<CompilerOutput> {
270 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
271 // is out of scope.
Victor Hsieh13333e82021-09-03 15:17:32 -0700272 let authfs_config = build_authfs_config(fd_annotation);
Victor Hsieh51789de2021-08-06 16:50:49 -0700273 let authfs = authfs_service.mount(&authfs_config)?;
274
275 // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
276 // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
277 // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
278 // setup.
279 let fd_mapping =
280 open_authfs_files_for_fd_mapping(&authfs, &authfs_config).context("Open on authfs")?;
281
282 let jail =
283 spawn_jailed_task(compiler_path, compiler_args, fd_mapping).context("Spawn dex2oat")?;
284 let jail_result = jail.wait();
285
Victor Hsieh6e340382021-08-13 12:18:02 -0700286 let parcel_fds = parse_compiler_args(&authfs, compiler_args)?;
287 let oat_file: &File = parcel_fds.oat.as_ref();
288 let vdex_file: &File = parcel_fds.vdex.as_ref();
289 let image_file: &File = parcel_fds.image.as_ref();
Victor Hsieh51789de2021-08-06 16:50:49 -0700290
291 match jail_result {
Victor Hsieh6e340382021-08-13 12:18:02 -0700292 Ok(()) => Ok(CompilerOutput::Digests {
Victor Hsieh9ed27182021-08-25 15:52:42 -0700293 oat: fsverity::measure(oat_file.as_raw_fd())?,
294 vdex: fsverity::measure(vdex_file.as_raw_fd())?,
295 image: fsverity::measure(image_file.as_raw_fd())?,
Victor Hsieh6e340382021-08-13 12:18:02 -0700296 }),
Victor Hsieh51789de2021-08-06 16:50:49 -0700297 Err(minijail::Error::ReturnCode(exit_code)) => {
Victor Hsieh6e340382021-08-13 12:18:02 -0700298 error!("dex2oat failed with exit code {}", exit_code);
299 Ok(CompilerOutput::ExitCode(exit_code as i8))
Victor Hsieh51789de2021-08-06 16:50:49 -0700300 }
301 Err(e) => {
302 bail!("Unexpected minijail error: {}", e)
303 }
304 }
305}
306
Victor Hsieh6e340382021-08-13 12:18:02 -0700307fn parse_compiler_args(
308 authfs: &Strong<dyn IAuthFs>,
309 args: &[String],
310) -> Result<CompilerOutputParcelFds> {
311 const OAT_FD_PREFIX: &str = "--oat-fd=";
312 const VDEX_FD_PREFIX: &str = "--output-vdex-fd=";
313 const IMAGE_FD_PREFIX: &str = "--image-fd=";
314 const APP_IMAGE_FD_PREFIX: &str = "--app-image-fd=";
315
316 let mut oat = None;
317 let mut vdex = None;
318 let mut image = None;
319
320 for arg in args {
321 if let Some(value) = arg.strip_prefix(OAT_FD_PREFIX) {
322 let fd = value.parse::<RawFd>().context("Invalid --oat-fd flag")?;
323 debug_assert!(oat.is_none());
324 oat = Some(authfs.openFile(fd, false)?);
325 } else if let Some(value) = arg.strip_prefix(VDEX_FD_PREFIX) {
326 let fd = value.parse::<RawFd>().context("Invalid --output-vdex-fd flag")?;
327 debug_assert!(vdex.is_none());
328 vdex = Some(authfs.openFile(fd, false)?);
329 } else if let Some(value) = arg.strip_prefix(IMAGE_FD_PREFIX) {
330 let fd = value.parse::<RawFd>().context("Invalid --image-fd flag")?;
331 debug_assert!(image.is_none());
332 image = Some(authfs.openFile(fd, false)?);
333 } else if let Some(value) = arg.strip_prefix(APP_IMAGE_FD_PREFIX) {
334 let fd = value.parse::<RawFd>().context("Invalid --app-image-fd flag")?;
335 debug_assert!(image.is_none());
336 image = Some(authfs.openFile(fd, false)?);
337 }
338 }
339
340 Ok(CompilerOutputParcelFds {
341 oat: oat.ok_or_else(|| anyhow!("Missing --oat-fd"))?,
342 vdex: vdex.ok_or_else(|| anyhow!("Missing --vdex-fd"))?,
343 image: image.ok_or_else(|| anyhow!("Missing --image-fd or --app-image-fd"))?,
344 })
345}
346
Victor Hsieh13333e82021-09-03 15:17:32 -0700347fn build_authfs_config(fd_annotation: &FdAnnotation) -> AuthFsConfig {
Victor Hsieh51789de2021-08-06 16:50:49 -0700348 AuthFsConfig {
Victor Hsiehf9968692021-11-18 11:34:39 -0800349 port: FD_SERVER_PORT,
Victor Hsieh13333e82021-09-03 15:17:32 -0700350 inputFdAnnotations: fd_annotation
351 .input_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700352 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700353 .map(|fd| InputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700354 .collect(),
Victor Hsieh13333e82021-09-03 15:17:32 -0700355 outputFdAnnotations: fd_annotation
356 .output_fds
Victor Hsieh51789de2021-08-06 16:50:49 -0700357 .iter()
Victor Hsieh13333e82021-09-03 15:17:32 -0700358 .map(|fd| OutputFdAnnotation { fd: *fd })
Victor Hsieh51789de2021-08-06 16:50:49 -0700359 .collect(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800360 ..Default::default()
Victor Hsieh51789de2021-08-06 16:50:49 -0700361 }
362}
363
364fn open_authfs_files_for_fd_mapping(
365 authfs: &Strong<dyn IAuthFs>,
366 config: &AuthFsConfig,
367) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
368 let mut fd_mapping = Vec::new();
369
370 let results: Result<Vec<_>> = config
371 .inputFdAnnotations
372 .iter()
373 .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
374 .collect();
375 fd_mapping.append(&mut results?);
376
377 let results: Result<Vec<_>> = config
378 .outputFdAnnotations
379 .iter()
380 .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
381 .collect();
382 fd_mapping.append(&mut results?);
383
384 Ok(fd_mapping)
385}
386
387fn spawn_jailed_task(
388 executable: &Path,
389 args: &[String],
390 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
391) -> Result<Minijail> {
392 // TODO(b/185175567): Run in a more restricted sandbox.
393 let jail = Minijail::new()?;
394 let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
395 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
396 Ok(jail)
397}