blob: d165599d1951241a87235460dd514d3e8d50f71f [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
Alan Stokesfadcef22022-01-24 17:00:59 +000017use anyhow::{anyhow, bail, Context, Result};
Victor Hsieh616f8222022-01-14 13:06:32 -080018use 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;
Alan Stokes2d2e4db2022-01-28 16:41:52 +000021use rustutils::system_properties;
Alan Stokesfadcef22022-01-24 17:00:59 +000022use std::collections::HashMap;
Victor Hsiehf9968692021-11-18 11:34:39 -080023use std::env;
Alan Stokes92472512022-01-04 11:48:38 +000024use std::ffi::OsString;
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
28use 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,
Victor Hsieh616f8222022-01-14 13:06:32 -080031 OutputDirFdAnnotation::OutputDirFdAnnotation,
Victor Hsieh015bcb52021-11-17 17:28:01 -080032 },
Victor Hsieh015bcb52021-11-17 17:28:01 -080033 IAuthFsService::IAuthFsService,
Victor Hsieh51789de2021-08-06 16:50:49 -070034};
Alan Stokes0e82b502022-08-08 14:44:48 +010035use binder::Strong;
Alan Stokes2d2e4db2022-01-28 16:41:52 +000036use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
Alan Stokes46a1dff2021-12-14 10:56:05 +000037use compos_common::odrefresh::ExitCode;
Victor Hsieh51789de2021-08-06 16:50:49 -070038
Victor Hsiehf9968692021-11-18 11:34:39 -080039const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
40
Alan Stokes46a1dff2021-12-14 10:56:05 +000041pub struct OdrefreshContext<'a> {
Alan Stokes2d2e4db2022-01-28 16:41:52 +000042 compilation_mode: CompilationMode,
Victor Hsiehf9968692021-11-18 11:34:39 -080043 system_dir_fd: i32,
Victor Hsieh64d88622022-09-21 17:32:00 -070044 system_ext_dir_fd: Option<i32>,
Victor Hsiehf9968692021-11-18 11:34:39 -080045 output_dir_fd: i32,
Alan Stokes9646db92021-12-14 13:22:33 +000046 staging_dir_fd: i32,
Alan Stokes46a1dff2021-12-14 10:56:05 +000047 target_dir_name: &'a str,
48 zygote_arch: &'a str,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080049 system_server_compiler_filter: &'a str,
Alan Stokes46a1dff2021-12-14 10:56:05 +000050}
51
52impl<'a> OdrefreshContext<'a> {
Victor Hsieh64d88622022-09-21 17:32:00 -070053 #[allow(clippy::too_many_arguments)]
Alan Stokes46a1dff2021-12-14 10:56:05 +000054 pub fn new(
Alan Stokes2d2e4db2022-01-28 16:41:52 +000055 compilation_mode: CompilationMode,
Alan Stokes46a1dff2021-12-14 10:56:05 +000056 system_dir_fd: i32,
Victor Hsieh64d88622022-09-21 17:32:00 -070057 system_ext_dir_fd: Option<i32>,
Alan Stokes46a1dff2021-12-14 10:56:05 +000058 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> {
Alan Stokes2d2e4db2022-01-28 16:41:52 +000064 if compilation_mode != CompilationMode::NORMAL_COMPILE {
Andrew Scull65ddfc42022-02-14 21:03:58 +000065 // Conservatively check debuggability.
66 let debuggable =
67 system_properties::read_bool("ro.boot.microdroid.app_debuggable", false)
68 .unwrap_or(false);
Alan Stokes2d2e4db2022-01-28 16:41:52 +000069 if !debuggable {
70 bail!("Requested compilation mode only available in debuggable VMs");
71 }
72 }
73
Alan Stokes46a1dff2021-12-14 10:56:05 +000074 if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
75 bail!("The remote FDs are expected to be non-negative");
76 }
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080077 if !matches!(zygote_arch, "zygote64" | "zygote64_32") {
Alan Stokes46a1dff2021-12-14 10:56:05 +000078 bail!("Invalid zygote arch");
79 }
Alan Stokes35bac3c2021-12-16 14:37:24 +000080 // Disallow any sort of path traversal
81 if target_dir_name.contains(path::MAIN_SEPARATOR) {
82 bail!("Invalid target directory {}", target_dir_name);
83 }
84
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080085 // We're not validating/allowlisting the compiler filter, and just assume the compiler will
86 // reject an invalid string. We need to accept "verify" filter anyway, and potential
87 // performance degration by the attacker is not currently in scope. This also allows ART to
88 // specify new compiler filter and configure through system property without change to
89 // CompOS.
90
91 Ok(Self {
Alan Stokes2d2e4db2022-01-28 16:41:52 +000092 compilation_mode,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080093 system_dir_fd,
Victor Hsieh64d88622022-09-21 17:32:00 -070094 system_ext_dir_fd,
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -080095 output_dir_fd,
96 staging_dir_fd,
97 target_dir_name,
98 zygote_arch,
99 system_server_compiler_filter,
100 })
Alan Stokes46a1dff2021-12-14 10:56:05 +0000101 }
102}
103
Victor Hsiehec38ae22022-02-10 00:06:26 +0000104pub fn odrefresh<F>(
Alan Stokes46a1dff2021-12-14 10:56:05 +0000105 odrefresh_path: &Path,
106 context: OdrefreshContext,
Victor Hsiehf9968692021-11-18 11:34:39 -0800107 authfs_service: Strong<dyn IAuthFsService>,
Victor Hsiehec38ae22022-02-10 00:06:26 +0000108 success_fn: F,
109) -> Result<ExitCode>
110where
111 F: FnOnce(PathBuf) -> Result<()>,
112{
Victor Hsiehf9968692021-11-18 11:34:39 -0800113 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
114 // is out of scope.
Victor Hsieh64d88622022-09-21 17:32:00 -0700115
116 let mut input_dir_fd_annotations = vec![InputDirFdAnnotation {
117 fd: context.system_dir_fd,
118 // Use the 0th APK of the extra_apks in compos/apk/assets/vm_config*.json
119 manifestPath: "/mnt/extra-apk/0/assets/build_manifest.pb".to_string(),
120 prefix: "system/".to_string(),
121 }];
122 if let Some(fd) = context.system_ext_dir_fd {
123 input_dir_fd_annotations.push(InputDirFdAnnotation {
124 fd,
125 // Use the 1st APK of the extra_apks in compos/apk/assets/vm_config_system_ext_*.json
126 manifestPath: "/mnt/extra-apk/1/assets/build_manifest.pb".to_string(),
127 prefix: "system_ext/".to_string(),
128 });
129 }
130
Victor Hsiehf9968692021-11-18 11:34:39 -0800131 let authfs_config = AuthFsConfig {
132 port: FD_SERVER_PORT,
Victor Hsieh64d88622022-09-21 17:32:00 -0700133 inputDirFdAnnotations: input_dir_fd_annotations,
Alan Stokes9646db92021-12-14 13:22:33 +0000134 outputDirFdAnnotations: vec![
Alan Stokes46a1dff2021-12-14 10:56:05 +0000135 OutputDirFdAnnotation { fd: context.output_dir_fd },
136 OutputDirFdAnnotation { fd: context.staging_dir_fd },
Alan Stokes9646db92021-12-14 13:22:33 +0000137 ],
Victor Hsiehf9968692021-11-18 11:34:39 -0800138 ..Default::default()
139 };
140 let authfs = authfs_service.mount(&authfs_config)?;
141 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
142
Alan Stokesfadcef22022-01-24 17:00:59 +0000143 // Make a copy of our environment as the basis of the one we will give odrefresh
144 let mut odrefresh_vars = EnvMap::from_current_env();
145
Victor Hsiehf9968692021-11-18 11:34:39 -0800146 let mut android_root = mountpoint.clone();
Alan Stokes46a1dff2021-12-14 10:56:05 +0000147 android_root.push(context.system_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800148 android_root.push("system");
Alan Stokesfadcef22022-01-24 17:00:59 +0000149 odrefresh_vars.set("ANDROID_ROOT", path_to_str(&android_root)?);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000150 debug!("ANDROID_ROOT={:?}", &android_root);
Victor Hsiehf9968692021-11-18 11:34:39 -0800151
Victor Hsieh64d88622022-09-21 17:32:00 -0700152 if let Some(fd) = context.system_ext_dir_fd {
153 let mut system_ext_root = mountpoint.clone();
154 system_ext_root.push(fd.to_string());
155 system_ext_root.push("system_ext");
156 odrefresh_vars.set("SYSTEM_EXT_ROOT", path_to_str(&system_ext_root)?);
157 debug!("SYSTEM_EXT_ROOT={:?}", &system_ext_root);
158 }
159
Alan Stokes46a1dff2021-12-14 10:56:05 +0000160 let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
Alan Stokesfadcef22022-01-24 17:00:59 +0000161 odrefresh_vars.set("ART_APEX_DATA", path_to_str(&art_apex_data)?);
Alan Stokes46a1dff2021-12-14 10:56:05 +0000162 debug!("ART_APEX_DATA={:?}", &art_apex_data);
Victor Hsieh64df53d2021-11-30 17:09:51 -0800163
Alan Stokes46a1dff2021-12-14 10:56:05 +0000164 let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
Victor Hsiehf9968692021-11-18 11:34:39 -0800165
Alan Stokesfadcef22022-01-24 17:00:59 +0000166 set_classpaths(&mut odrefresh_vars, &android_root)?;
Alan Stokes92472512022-01-04 11:48:38 +0000167
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800168 let mut args = vec![
Victor Hsiehf9968692021-11-18 11:34:39 -0800169 "odrefresh".to_string(),
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000170 "--compilation-os-mode".to_string(),
Alan Stokes46a1dff2021-12-14 10:56:05 +0000171 format!("--zygote-arch={}", context.zygote_arch),
172 format!("--dalvik-cache={}", context.target_dir_name),
Victor Hsiehf9968692021-11-18 11:34:39 -0800173 format!("--staging-dir={}", staging_dir.display()),
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800174 "--no-refresh".to_string(),
Victor Hsiehf9968692021-11-18 11:34:39 -0800175 ];
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800176
177 if !context.system_server_compiler_filter.is_empty() {
178 args.push(format!(
179 "--system-server-compiler-filter={}",
180 context.system_server_compiler_filter
181 ));
182 }
Alan Stokes48c1d2b2022-01-10 15:54:04 +0000183
Alan Stokes2d2e4db2022-01-28 16:41:52 +0000184 let compile_flag = match context.compilation_mode {
185 CompilationMode::NORMAL_COMPILE => "--compile",
186 CompilationMode::TEST_COMPILE => "--force-compile",
187 other => bail!("Unknown compilation mode {:?}", other),
188 };
189 args.push(compile_flag.to_string());
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800190
Alan Stokes9646db92021-12-14 13:22:33 +0000191 debug!("Running odrefresh with args: {:?}", &args);
Alan Stokesfadcef22022-01-24 17:00:59 +0000192 let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
Victor Hsiehf9968692021-11-18 11:34:39 -0800193 .context("Spawn odrefresh")?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000194 let exit_code = match jail.wait() {
Alan Stokes2d2e4db2022-01-28 16:41:52 +0000195 Ok(_) => 0,
196 Err(minijail::Error::ReturnCode(exit_code)) => exit_code,
197 Err(e) => bail!("Unexpected minijail error: {}", e),
198 };
Alan Stokes46a1dff2021-12-14 10:56:05 +0000199
Alan Stokes126fd512021-12-16 15:00:01 +0000200 let exit_code = ExitCode::from_i32(exit_code.into())?;
Alan Stokes46a1dff2021-12-14 10:56:05 +0000201 info!("odrefresh exited with {:?}", exit_code);
202
203 if exit_code == ExitCode::CompilationSuccess {
Alan Stokes46a1dff2021-12-14 10:56:05 +0000204 let target_dir = art_apex_data.join(context.target_dir_name);
Victor Hsiehec38ae22022-02-10 00:06:26 +0000205 success_fn(target_dir)?;
Victor Hsiehf9968692021-11-18 11:34:39 -0800206 }
Alan Stokes46a1dff2021-12-14 10:56:05 +0000207
208 Ok(exit_code)
209}
210
Alan Stokesfadcef22022-01-24 17:00:59 +0000211fn path_to_str(path: &Path) -> Result<&str> {
212 path.to_str().ok_or_else(|| anyhow!("Bad path {:?}", path))
213}
214
215fn set_classpaths(odrefresh_vars: &mut EnvMap, android_root: &Path) -> Result<()> {
Alan Stokes92472512022-01-04 11:48:38 +0000216 let export_lines = run_derive_classpath(android_root)?;
Alan Stokesfadcef22022-01-24 17:00:59 +0000217 load_classpath_vars(odrefresh_vars, &export_lines)
Alan Stokes92472512022-01-04 11:48:38 +0000218}
219
220fn run_derive_classpath(android_root: &Path) -> Result<String> {
221 let classpaths_root = android_root.join("etc/classpaths");
222
223 let mut bootclasspath_arg = OsString::new();
224 bootclasspath_arg.push("--bootclasspath-fragment=");
225 bootclasspath_arg.push(classpaths_root.join("bootclasspath.pb"));
226
227 let mut systemserverclasspath_arg = OsString::new();
228 systemserverclasspath_arg.push("--systemserverclasspath-fragment=");
229 systemserverclasspath_arg.push(classpaths_root.join("systemserverclasspath.pb"));
230
231 let result = Command::new("/apex/com.android.sdkext/bin/derive_classpath")
232 .arg(bootclasspath_arg)
233 .arg(systemserverclasspath_arg)
234 .arg("/proc/self/fd/1")
235 .output()
236 .context("Failed to run derive_classpath")?;
237
238 if !result.status.success() {
239 bail!("derive_classpath returned {}", result.status);
240 }
241
242 String::from_utf8(result.stdout).context("Converting derive_classpath output")
243}
244
Alan Stokesfadcef22022-01-24 17:00:59 +0000245fn load_classpath_vars(odrefresh_vars: &mut EnvMap, export_lines: &str) -> Result<()> {
Alan Stokes92472512022-01-04 11:48:38 +0000246 // Each line should be in the format "export <var name> <value>"
247 let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
248 for line in export_lines.lines() {
249 if let Some(captures) = pattern.captures(line) {
250 let name = &captures[1];
251 let value = &captures[2];
Alan Stokesfadcef22022-01-24 17:00:59 +0000252 odrefresh_vars.set(name, value);
Alan Stokes92472512022-01-04 11:48:38 +0000253 } else {
254 warn!("Malformed line from derive_classpath: {}", line);
255 }
256 }
257
258 Ok(())
259}
260
Alan Stokesfadcef22022-01-24 17:00:59 +0000261fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
Victor Hsieh51789de2021-08-06 16:50:49 -0700262 // TODO(b/185175567): Run in a more restricted sandbox.
263 let jail = Minijail::new()?;
Alan Stokesfadcef22022-01-24 17:00:59 +0000264 let keep_fds = [];
265 let command = minijail::Command::new_for_path(executable, &keep_fds, args, Some(env_vars))?;
266 let _pid = jail.run_command(command)?;
Victor Hsieh51789de2021-08-06 16:50:49 -0700267 Ok(jail)
268}
Alan Stokesfadcef22022-01-24 17:00:59 +0000269
270struct EnvMap(HashMap<String, String>);
271
272impl EnvMap {
273 fn from_current_env() -> Self {
274 Self(env::vars().collect())
275 }
276
277 fn set(&mut self, key: &str, value: &str) {
278 self.0.insert(key.to_owned(), value.to_owned());
279 }
280
281 fn into_env(self) -> Vec<String> {
282 // execve() expects an array of "k=v" strings, rather than a list of (k, v) pairs.
283 self.0.into_iter().map(|(k, v)| k + "=" + &v).collect()
284 }
285}