blob: 6db31b6e5c0e7eea65a02376fcea7b059e64d3eb [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 Hsieh616f8222022-01-14 13:06:32 -080017use anyhow::{bail, Context, Result};
18use log::{debug, 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;
Victor Hsieh616f8222022-01-14 13:06:32 -080023use std::fs::read_dir;
24use std::os::unix::io::AsRawFd;
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 Hsieh51789de2021-08-06 16:50:49 -070030use authfs_aidl_interface::aidl::com::android::virt::fs::{
Victor Hsieh015bcb52021-11-17 17:28:01 -080031 AuthFsConfig::{
Victor Hsiehf9968692021-11-18 11:34:39 -080032 AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
Victor Hsieh616f8222022-01-14 13:06:32 -080033 OutputDirFdAnnotation::OutputDirFdAnnotation,
Victor Hsieh015bcb52021-11-17 17:28:01 -080034 },
Victor Hsieh015bcb52021-11-17 17:28:01 -080035 IAuthFsService::IAuthFsService,
Victor Hsieh51789de2021-08-06 16:50:49 -070036};
37use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Alan Stokes46a1dff2021-12-14 10:56:05 +000038use compos_common::odrefresh::ExitCode;
Victor Hsieh51789de2021-08-06 16:50:49 -070039
Victor Hsiehf9968692021-11-18 11:34:39 -080040const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
41
Victor Hsieh51789de2021-08-06 16:50:49 -070042/// The number that represents the file descriptor number expecting by the task. The number may be
43/// meaningless in the current process.
44pub type PseudoRawFd = i32;
45
Alan Stokes46a1dff2021-12-14 10:56:05 +000046pub struct OdrefreshContext<'a> {
Victor Hsiehf9968692021-11-18 11:34:39 -080047 system_dir_fd: i32,
48 output_dir_fd: i32,
Alan Stokes9646db92021-12-14 13:22:33 +000049 staging_dir_fd: i32,
Alan Stokes46a1dff2021-12-14 10:56:05 +000050 target_dir_name: &'a str,
51 zygote_arch: &'a str,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080052 system_server_compiler_filter: &'a str,
Alan Stokes46a1dff2021-12-14 10:56:05 +000053}
54
55impl<'a> OdrefreshContext<'a> {
56 pub fn new(
57 system_dir_fd: i32,
58 output_dir_fd: i32,
59 staging_dir_fd: i32,
60 target_dir_name: &'a str,
61 zygote_arch: &'a str,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080062 system_server_compiler_filter: &'a str,
Alan Stokes46a1dff2021-12-14 10:56:05 +000063 ) -> Result<Self> {
64 if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
65 bail!("The remote FDs are expected to be non-negative");
66 }
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080067 if !matches!(zygote_arch, "zygote64" | "zygote64_32") {
Alan Stokes46a1dff2021-12-14 10:56:05 +000068 bail!("Invalid zygote arch");
69 }
Alan Stokes35bac3c2021-12-16 14:37:24 +000070 // Disallow any sort of path traversal
71 if target_dir_name.contains(path::MAIN_SEPARATOR) {
72 bail!("Invalid target directory {}", target_dir_name);
73 }
74
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080075 // We're not validating/allowlisting the compiler filter, and just assume the compiler will
76 // reject an invalid string. We need to accept "verify" filter anyway, and potential
77 // performance degration by the attacker is not currently in scope. This also allows ART to
78 // specify new compiler filter and configure through system property without change to
79 // CompOS.
80
81 Ok(Self {
82 system_dir_fd,
83 output_dir_fd,
84 staging_dir_fd,
85 target_dir_name,
86 zygote_arch,
87 system_server_compiler_filter,
88 })
Alan Stokes46a1dff2021-12-14 10:56:05 +000089 }
90}
91
92pub fn odrefresh(
93 odrefresh_path: &Path,
94 context: OdrefreshContext,
Victor Hsiehf9968692021-11-18 11:34:39 -080095 authfs_service: Strong<dyn IAuthFsService>,
Alan Stokes46a1dff2021-12-14 10:56:05 +000096 signer: Signer,
97) -> Result<ExitCode> {
Victor Hsiehf9968692021-11-18 11:34:39 -080098 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
99 // is out of scope.
100 let authfs_config = AuthFsConfig {
101 port: FD_SERVER_PORT,
102 inputDirFdAnnotations: vec![InputDirFdAnnotation {
Alan Stokes46a1dff2021-12-14 10:56:05 +0000103 fd: context.system_dir_fd,
Victor Hsieh99782572022-01-05 15:38:33 -0800104 // 0 is the index of extra_apks in vm_config_extra_apk.json
105 manifestPath: "/mnt/extra-apk/0/assets/build_manifest.pb".to_string(),
106 prefix: "system/".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800107 }],
Alan Stokes9646db92021-12-14 13:22:33 +0000108 outputDirFdAnnotations: vec![
Alan Stokes46a1dff2021-12-14 10:56:05 +0000109 OutputDirFdAnnotation { fd: context.output_dir_fd },
110 OutputDirFdAnnotation { fd: context.staging_dir_fd },
Alan Stokes9646db92021-12-14 13:22:33 +0000111 ],
Victor Hsiehf9968692021-11-18 11:34:39 -0800112 ..Default::default()
113 };
114 let authfs = authfs_service.mount(&authfs_config)?;
115 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
116
117 let mut android_root = mountpoint.clone();
Alan Stokes46a1dff2021-12-14 10:56:05 +0000118 android_root.push(context.system_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800119 android_root.push("system");
120 env::set_var("ANDROID_ROOT", &android_root);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000121 debug!("ANDROID_ROOT={:?}", &android_root);
Victor Hsiehf9968692021-11-18 11:34:39 -0800122
Alan Stokes46a1dff2021-12-14 10:56:05 +0000123 let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
Victor Hsieh64df53d2021-11-30 17:09:51 -0800124 env::set_var("ART_APEX_DATA", &art_apex_data);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000125 debug!("ART_APEX_DATA={:?}", &art_apex_data);
Victor Hsieh64df53d2021-11-30 17:09:51 -0800126
Alan Stokes46a1dff2021-12-14 10:56:05 +0000127 let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800128
Alan Stokes92472512022-01-04 11:48:38 +0000129 set_classpaths(&android_root)?;
130
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800131 let mut args = vec![
Victor Hsiehf9968692021-11-18 11:34:39 -0800132 "odrefresh".to_string(),
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000133 "--compilation-os-mode".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 Hsiehf9968692021-11-18 11:34:39 -0800136 format!("--staging-dir={}", staging_dir.display()),
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800137 "--no-refresh".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800138 ];
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800139
140 if !context.system_server_compiler_filter.is_empty() {
141 args.push(format!(
142 "--system-server-compiler-filter={}",
143 context.system_server_compiler_filter
144 ));
145 }
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000146
147 args.push("--compile".to_string());
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800148
Alan Stokes9646db92021-12-14 13:22:33 +0000149 debug!("Running odrefresh with args: {:?}", &args);
Victor Hsiehf9968692021-11-18 11:34:39 -0800150 let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
151 .context("Spawn odrefresh")?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000152 let exit_code = match jail.wait() {
153 Ok(_) => Result::<u8>::Ok(0),
154 Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
Victor Hsiehf9968692021-11-18 11:34:39 -0800155 Err(e) => {
156 bail!("Unexpected minijail error: {}", e)
157 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000158 }?;
159
Alan Stokes126fd512021-12-16 15:00:01 +0000160 let exit_code = ExitCode::from_i32(exit_code.into())?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000161 info!("odrefresh exited with {:?}", exit_code);
162
163 if exit_code == ExitCode::CompilationSuccess {
164 // authfs only shows us the files we created, so it's ok to just sign everything under
165 // the target directory.
166 let target_dir = art_apex_data.join(context.target_dir_name);
167 let mut artifact_signer = ArtifactSigner::new(&target_dir);
168 add_artifacts(&target_dir, &mut artifact_signer)?;
169
170 artifact_signer.write_info_and_signature(signer, &target_dir.join("compos.info"))?;
Victor Hsiehf9968692021-11-18 11:34:39 -0800171 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000172
173 Ok(exit_code)
174}
175
Alan Stokes92472512022-01-04 11:48:38 +0000176fn set_classpaths(android_root: &Path) -> Result<()> {
177 let export_lines = run_derive_classpath(android_root)?;
178 load_classpath_vars(&export_lines)
179}
180
181fn run_derive_classpath(android_root: &Path) -> Result<String> {
182 let classpaths_root = android_root.join("etc/classpaths");
183
184 let mut bootclasspath_arg = OsString::new();
185 bootclasspath_arg.push("--bootclasspath-fragment=");
186 bootclasspath_arg.push(classpaths_root.join("bootclasspath.pb"));
187
188 let mut systemserverclasspath_arg = OsString::new();
189 systemserverclasspath_arg.push("--systemserverclasspath-fragment=");
190 systemserverclasspath_arg.push(classpaths_root.join("systemserverclasspath.pb"));
191
192 let result = Command::new("/apex/com.android.sdkext/bin/derive_classpath")
193 .arg(bootclasspath_arg)
194 .arg(systemserverclasspath_arg)
195 .arg("/proc/self/fd/1")
196 .output()
197 .context("Failed to run derive_classpath")?;
198
199 if !result.status.success() {
200 bail!("derive_classpath returned {}", result.status);
201 }
202
203 String::from_utf8(result.stdout).context("Converting derive_classpath output")
204}
205
206fn load_classpath_vars(export_lines: &str) -> Result<()> {
207 // Each line should be in the format "export <var name> <value>"
208 let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
209 for line in export_lines.lines() {
210 if let Some(captures) = pattern.captures(line) {
211 let name = &captures[1];
212 let value = &captures[2];
213 // TODO(b/213416778) Don't modify our env, construct a fresh one for odrefresh
214 env::set_var(name, value);
215 } else {
216 warn!("Malformed line from derive_classpath: {}", line);
217 }
218 }
219
220 Ok(())
221}
222
Alan Stokes46a1dff2021-12-14 10:56:05 +0000223fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
224 for entry in
225 read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
226 {
227 let entry = entry?;
228 let file_type = entry.file_type()?;
229 if file_type.is_dir() {
230 add_artifacts(&entry.path(), artifact_signer)?;
231 } else if file_type.is_file() {
232 artifact_signer.add_artifact(&entry.path())?;
233 } else {
234 // authfs shouldn't create anything else, but just in case
235 bail!("Unexpected file type in artifacts: {:?}", entry);
236 }
237 }
238 Ok(())
Victor Hsiehf9968692021-11-18 11:34:39 -0800239}
240
Victor Hsieh51789de2021-08-06 16:50:49 -0700241fn spawn_jailed_task(
242 executable: &Path,
243 args: &[String],
244 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
245) -> Result<Minijail> {
246 // TODO(b/185175567): Run in a more restricted sandbox.
247 let jail = Minijail::new()?;
248 let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
249 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
250 Ok(jail)
251}