1 /*
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
17 use anyhow::{anyhow, bail, Context, Result};
18 use log::{debug, info, warn};
19 use minijail::{self, Minijail};
20 use regex::Regex;
21 use rustutils::system_properties;
22 use std::collections::HashMap;
23 use std::env;
24 use std::ffi::OsString;
25 use std::path::{self, Path, PathBuf};
26 use std::process::Command;
27
28 use authfs_aidl_interface::aidl::com::android::virt::fs::{
29 AuthFsConfig::{
30 AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
31 OutputDirFdAnnotation::OutputDirFdAnnotation,
32 },
33 IAuthFsService::IAuthFsService,
34 };
35 use authfs_aidl_interface::binder::Strong;
36 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
37 use compos_common::odrefresh::ExitCode;
38
39 const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
40
41 pub struct OdrefreshContext<'a> {
42 compilation_mode: CompilationMode,
43 system_dir_fd: i32,
44 output_dir_fd: i32,
45 staging_dir_fd: i32,
46 target_dir_name: &'a str,
47 zygote_arch: &'a str,
48 system_server_compiler_filter: &'a str,
49 }
50
51 impl<'a> OdrefreshContext<'a> {
new( compilation_mode: CompilationMode, system_dir_fd: i32, output_dir_fd: i32, staging_dir_fd: i32, target_dir_name: &'a str, zygote_arch: &'a str, system_server_compiler_filter: &'a str, ) -> Result<Self>52 pub fn new(
53 compilation_mode: CompilationMode,
54 system_dir_fd: i32,
55 output_dir_fd: i32,
56 staging_dir_fd: i32,
57 target_dir_name: &'a str,
58 zygote_arch: &'a str,
59 system_server_compiler_filter: &'a str,
60 ) -> Result<Self> {
61 if compilation_mode != CompilationMode::NORMAL_COMPILE {
62 // Conservatively check debuggability.
63 let debuggable =
64 system_properties::read_bool("ro.boot.microdroid.app_debuggable", false)
65 .unwrap_or(false);
66 if !debuggable {
67 bail!("Requested compilation mode only available in debuggable VMs");
68 }
69 }
70
71 if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
72 bail!("The remote FDs are expected to be non-negative");
73 }
74 if !matches!(zygote_arch, "zygote64" | "zygote64_32") {
75 bail!("Invalid zygote arch");
76 }
77 // Disallow any sort of path traversal
78 if target_dir_name.contains(path::MAIN_SEPARATOR) {
79 bail!("Invalid target directory {}", target_dir_name);
80 }
81
82 // We're not validating/allowlisting the compiler filter, and just assume the compiler will
83 // reject an invalid string. We need to accept "verify" filter anyway, and potential
84 // performance degration by the attacker is not currently in scope. This also allows ART to
85 // specify new compiler filter and configure through system property without change to
86 // CompOS.
87
88 Ok(Self {
89 compilation_mode,
90 system_dir_fd,
91 output_dir_fd,
92 staging_dir_fd,
93 target_dir_name,
94 zygote_arch,
95 system_server_compiler_filter,
96 })
97 }
98 }
99
odrefresh<F>( odrefresh_path: &Path, context: OdrefreshContext, authfs_service: Strong<dyn IAuthFsService>, success_fn: F, ) -> Result<ExitCode> where F: FnOnce(PathBuf) -> Result<()>,100 pub fn odrefresh<F>(
101 odrefresh_path: &Path,
102 context: OdrefreshContext,
103 authfs_service: Strong<dyn IAuthFsService>,
104 success_fn: F,
105 ) -> Result<ExitCode>
106 where
107 F: FnOnce(PathBuf) -> Result<()>,
108 {
109 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
110 // is out of scope.
111 let authfs_config = AuthFsConfig {
112 port: FD_SERVER_PORT,
113 inputDirFdAnnotations: vec![InputDirFdAnnotation {
114 fd: context.system_dir_fd,
115 // 0 is the index of extra_apks in vm_config_extra_apk.json
116 manifestPath: "/mnt/extra-apk/0/assets/build_manifest.pb".to_string(),
117 prefix: "system/".to_string(),
118 }],
119 outputDirFdAnnotations: vec![
120 OutputDirFdAnnotation { fd: context.output_dir_fd },
121 OutputDirFdAnnotation { fd: context.staging_dir_fd },
122 ],
123 ..Default::default()
124 };
125 let authfs = authfs_service.mount(&authfs_config)?;
126 let mountpoint = PathBuf::from(authfs.getMountPoint()?);
127
128 // Make a copy of our environment as the basis of the one we will give odrefresh
129 let mut odrefresh_vars = EnvMap::from_current_env();
130
131 let mut android_root = mountpoint.clone();
132 android_root.push(context.system_dir_fd.to_string());
133 android_root.push("system");
134 odrefresh_vars.set("ANDROID_ROOT", path_to_str(&android_root)?);
135 debug!("ANDROID_ROOT={:?}", &android_root);
136
137 let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
138 odrefresh_vars.set("ART_APEX_DATA", path_to_str(&art_apex_data)?);
139 debug!("ART_APEX_DATA={:?}", &art_apex_data);
140
141 let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
142
143 set_classpaths(&mut odrefresh_vars, &android_root)?;
144
145 let mut args = vec![
146 "odrefresh".to_string(),
147 "--compilation-os-mode".to_string(),
148 format!("--zygote-arch={}", context.zygote_arch),
149 format!("--dalvik-cache={}", context.target_dir_name),
150 format!("--staging-dir={}", staging_dir.display()),
151 "--no-refresh".to_string(),
152 ];
153
154 if !context.system_server_compiler_filter.is_empty() {
155 args.push(format!(
156 "--system-server-compiler-filter={}",
157 context.system_server_compiler_filter
158 ));
159 }
160
161 let compile_flag = match context.compilation_mode {
162 CompilationMode::NORMAL_COMPILE => "--compile",
163 CompilationMode::TEST_COMPILE => "--force-compile",
164 other => bail!("Unknown compilation mode {:?}", other),
165 };
166 args.push(compile_flag.to_string());
167
168 debug!("Running odrefresh with args: {:?}", &args);
169 let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
170 .context("Spawn odrefresh")?;
171 let exit_code = match jail.wait() {
172 Ok(_) => 0,
173 Err(minijail::Error::ReturnCode(exit_code)) => exit_code,
174 Err(e) => bail!("Unexpected minijail error: {}", e),
175 };
176
177 let exit_code = ExitCode::from_i32(exit_code.into())?;
178 info!("odrefresh exited with {:?}", exit_code);
179
180 if exit_code == ExitCode::CompilationSuccess {
181 let target_dir = art_apex_data.join(context.target_dir_name);
182 success_fn(target_dir)?;
183 }
184
185 Ok(exit_code)
186 }
187
path_to_str(path: &Path) -> Result<&str>188 fn path_to_str(path: &Path) -> Result<&str> {
189 path.to_str().ok_or_else(|| anyhow!("Bad path {:?}", path))
190 }
191
set_classpaths(odrefresh_vars: &mut EnvMap, android_root: &Path) -> Result<()>192 fn set_classpaths(odrefresh_vars: &mut EnvMap, android_root: &Path) -> Result<()> {
193 let export_lines = run_derive_classpath(android_root)?;
194 load_classpath_vars(odrefresh_vars, &export_lines)
195 }
196
run_derive_classpath(android_root: &Path) -> Result<String>197 fn run_derive_classpath(android_root: &Path) -> Result<String> {
198 let classpaths_root = android_root.join("etc/classpaths");
199
200 let mut bootclasspath_arg = OsString::new();
201 bootclasspath_arg.push("--bootclasspath-fragment=");
202 bootclasspath_arg.push(classpaths_root.join("bootclasspath.pb"));
203
204 let mut systemserverclasspath_arg = OsString::new();
205 systemserverclasspath_arg.push("--systemserverclasspath-fragment=");
206 systemserverclasspath_arg.push(classpaths_root.join("systemserverclasspath.pb"));
207
208 let result = Command::new("/apex/com.android.sdkext/bin/derive_classpath")
209 .arg(bootclasspath_arg)
210 .arg(systemserverclasspath_arg)
211 .arg("/proc/self/fd/1")
212 .output()
213 .context("Failed to run derive_classpath")?;
214
215 if !result.status.success() {
216 bail!("derive_classpath returned {}", result.status);
217 }
218
219 String::from_utf8(result.stdout).context("Converting derive_classpath output")
220 }
221
load_classpath_vars(odrefresh_vars: &mut EnvMap, export_lines: &str) -> Result<()>222 fn load_classpath_vars(odrefresh_vars: &mut EnvMap, export_lines: &str) -> Result<()> {
223 // Each line should be in the format "export <var name> <value>"
224 let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
225 for line in export_lines.lines() {
226 if let Some(captures) = pattern.captures(line) {
227 let name = &captures[1];
228 let value = &captures[2];
229 odrefresh_vars.set(name, value);
230 } else {
231 warn!("Malformed line from derive_classpath: {}", line);
232 }
233 }
234
235 Ok(())
236 }
237
spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail>238 fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
239 // TODO(b/185175567): Run in a more restricted sandbox.
240 let jail = Minijail::new()?;
241 let keep_fds = [];
242 let command = minijail::Command::new_for_path(executable, &keep_fds, args, Some(env_vars))?;
243 let _pid = jail.run_command(command)?;
244 Ok(jail)
245 }
246
247 struct EnvMap(HashMap<String, String>);
248
249 impl EnvMap {
from_current_env() -> Self250 fn from_current_env() -> Self {
251 Self(env::vars().collect())
252 }
253
set(&mut self, key: &str, value: &str)254 fn set(&mut self, key: &str, value: &str) {
255 self.0.insert(key.to_owned(), value.to_owned());
256 }
257
into_env(self) -> Vec<String>258 fn into_env(self) -> Vec<String> {
259 // execve() expects an array of "k=v" strings, rather than a list of (k, v) pairs.
260 self.0.into_iter().map(|(k, v)| k + "=" + &v).collect()
261 }
262 }
263