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