blob: 24266e6ea3c6d1e1e8f22d4f09c334a0e7a77d9e [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};
18use libc::getxattr;
Victor Hsieh51789de2021-08-06 16:50:49 -070019use log::error;
20use minijail::{self, Minijail};
Victor Hsieh6e340382021-08-13 12:18:02 -070021use std::ffi::CString;
22use std::fs::File;
23use std::io;
24use std::os::unix::io::{AsRawFd, RawFd};
Victor Hsieh51789de2021-08-06 16:50:49 -070025use std::path::Path;
26
27use authfs_aidl_interface::aidl::com::android::virt::fs::{
28 AuthFsConfig::AuthFsConfig, IAuthFs::IAuthFs, IAuthFsService::IAuthFsService,
29 InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
30};
31use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
32use compos_aidl_interface::aidl::com::android::compos::Metadata::Metadata;
33
34/// The number that represents the file descriptor number expecting by the task. The number may be
35/// meaningless in the current process.
36pub type PseudoRawFd = i32;
37
Victor Hsieh6e340382021-08-13 12:18:02 -070038const SHA256_HASH_SIZE: usize = 32;
39type Sha256Hash = [u8; SHA256_HASH_SIZE];
40
41pub enum CompilerOutput {
42 /// Fs-verity digests of output files, if the compiler finishes successfully.
43 Digests { oat: Sha256Hash, vdex: Sha256Hash, image: Sha256Hash },
44 /// Exit code returned by the compiler, if not 0.
45 ExitCode(i8),
46}
47
48struct CompilerOutputParcelFds {
49 oat: ParcelFileDescriptor,
50 vdex: ParcelFileDescriptor,
51 image: ParcelFileDescriptor,
52}
53
Victor Hsieh51789de2021-08-06 16:50:49 -070054/// Runs the compiler with given flags with file descriptors described in `metadata` retrieved via
55/// `authfs_service`. Returns exit code of the compiler process.
56pub fn compile(
57 compiler_path: &Path,
58 compiler_args: &[String],
59 authfs_service: Strong<dyn IAuthFsService>,
60 metadata: &Metadata,
Victor Hsieh6e340382021-08-13 12:18:02 -070061) -> Result<CompilerOutput> {
62 // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
63 // is out of scope.
Victor Hsieh51789de2021-08-06 16:50:49 -070064 let authfs_config = build_authfs_config(metadata);
65 let authfs = authfs_service.mount(&authfs_config)?;
66
67 // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
68 // on the host side. Since the local FD opened from authfs (e.g. /authfs/42) may not match
69 // the task's expectation, prepare a FD mapping and let minijail prepare the correct FD
70 // setup.
71 let fd_mapping =
72 open_authfs_files_for_fd_mapping(&authfs, &authfs_config).context("Open on authfs")?;
73
74 let jail =
75 spawn_jailed_task(compiler_path, compiler_args, fd_mapping).context("Spawn dex2oat")?;
76 let jail_result = jail.wait();
77
Victor Hsieh6e340382021-08-13 12:18:02 -070078 let parcel_fds = parse_compiler_args(&authfs, compiler_args)?;
79 let oat_file: &File = parcel_fds.oat.as_ref();
80 let vdex_file: &File = parcel_fds.vdex.as_ref();
81 let image_file: &File = parcel_fds.image.as_ref();
Victor Hsieh51789de2021-08-06 16:50:49 -070082
83 match jail_result {
Victor Hsieh6e340382021-08-13 12:18:02 -070084 Ok(()) => Ok(CompilerOutput::Digests {
85 oat: fsverity_measure(oat_file.as_raw_fd())?,
86 vdex: fsverity_measure(vdex_file.as_raw_fd())?,
87 image: fsverity_measure(image_file.as_raw_fd())?,
88 }),
Victor Hsieh51789de2021-08-06 16:50:49 -070089 Err(minijail::Error::ReturnCode(exit_code)) => {
Victor Hsieh6e340382021-08-13 12:18:02 -070090 error!("dex2oat failed with exit code {}", exit_code);
91 Ok(CompilerOutput::ExitCode(exit_code as i8))
Victor Hsieh51789de2021-08-06 16:50:49 -070092 }
93 Err(e) => {
94 bail!("Unexpected minijail error: {}", e)
95 }
96 }
97}
98
Victor Hsieh6e340382021-08-13 12:18:02 -070099fn parse_compiler_args(
100 authfs: &Strong<dyn IAuthFs>,
101 args: &[String],
102) -> Result<CompilerOutputParcelFds> {
103 const OAT_FD_PREFIX: &str = "--oat-fd=";
104 const VDEX_FD_PREFIX: &str = "--output-vdex-fd=";
105 const IMAGE_FD_PREFIX: &str = "--image-fd=";
106 const APP_IMAGE_FD_PREFIX: &str = "--app-image-fd=";
107
108 let mut oat = None;
109 let mut vdex = None;
110 let mut image = None;
111
112 for arg in args {
113 if let Some(value) = arg.strip_prefix(OAT_FD_PREFIX) {
114 let fd = value.parse::<RawFd>().context("Invalid --oat-fd flag")?;
115 debug_assert!(oat.is_none());
116 oat = Some(authfs.openFile(fd, false)?);
117 } else if let Some(value) = arg.strip_prefix(VDEX_FD_PREFIX) {
118 let fd = value.parse::<RawFd>().context("Invalid --output-vdex-fd flag")?;
119 debug_assert!(vdex.is_none());
120 vdex = Some(authfs.openFile(fd, false)?);
121 } else if let Some(value) = arg.strip_prefix(IMAGE_FD_PREFIX) {
122 let fd = value.parse::<RawFd>().context("Invalid --image-fd flag")?;
123 debug_assert!(image.is_none());
124 image = Some(authfs.openFile(fd, false)?);
125 } else if let Some(value) = arg.strip_prefix(APP_IMAGE_FD_PREFIX) {
126 let fd = value.parse::<RawFd>().context("Invalid --app-image-fd flag")?;
127 debug_assert!(image.is_none());
128 image = Some(authfs.openFile(fd, false)?);
129 }
130 }
131
132 Ok(CompilerOutputParcelFds {
133 oat: oat.ok_or_else(|| anyhow!("Missing --oat-fd"))?,
134 vdex: vdex.ok_or_else(|| anyhow!("Missing --vdex-fd"))?,
135 image: image.ok_or_else(|| anyhow!("Missing --image-fd or --app-image-fd"))?,
136 })
137}
138
Victor Hsieh51789de2021-08-06 16:50:49 -0700139fn build_authfs_config(metadata: &Metadata) -> AuthFsConfig {
140 AuthFsConfig {
141 port: 3264, // TODO: support dynamic port
142 inputFdAnnotations: metadata
143 .input_fd_annotations
144 .iter()
145 .map(|x| InputFdAnnotation { fd: x.fd, fileSize: x.file_size })
146 .collect(),
147 outputFdAnnotations: metadata
148 .output_fd_annotations
149 .iter()
150 .map(|x| OutputFdAnnotation { fd: x.fd })
151 .collect(),
152 }
153}
154
155fn open_authfs_files_for_fd_mapping(
156 authfs: &Strong<dyn IAuthFs>,
157 config: &AuthFsConfig,
158) -> Result<Vec<(ParcelFileDescriptor, PseudoRawFd)>> {
159 let mut fd_mapping = Vec::new();
160
161 let results: Result<Vec<_>> = config
162 .inputFdAnnotations
163 .iter()
164 .map(|annotation| Ok((authfs.openFile(annotation.fd, false)?, annotation.fd)))
165 .collect();
166 fd_mapping.append(&mut results?);
167
168 let results: Result<Vec<_>> = config
169 .outputFdAnnotations
170 .iter()
171 .map(|annotation| Ok((authfs.openFile(annotation.fd, true)?, annotation.fd)))
172 .collect();
173 fd_mapping.append(&mut results?);
174
175 Ok(fd_mapping)
176}
177
178fn spawn_jailed_task(
179 executable: &Path,
180 args: &[String],
181 fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
182) -> Result<Minijail> {
183 // TODO(b/185175567): Run in a more restricted sandbox.
184 let jail = Minijail::new()?;
185 let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
186 let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
187 Ok(jail)
188}
Victor Hsieh6e340382021-08-13 12:18:02 -0700189
190fn fsverity_measure(fd: RawFd) -> Result<Sha256Hash> {
191 // TODO(b/196635431): Unfortunately, the FUSE API doesn't allow authfs to implement the standard
192 // fs-verity ioctls. Until the kernel allows, use the alternative xattr that authfs provides.
193 let path = CString::new(format!("/proc/self/fd/{}", fd).as_str()).unwrap();
194 let name = CString::new("authfs.fsverity.digest").unwrap();
195 let mut buf = [0u8; SHA256_HASH_SIZE];
196 // SAFETY: getxattr should not write beyond the given buffer size.
197 let size = unsafe {
198 getxattr(path.as_ptr(), name.as_ptr(), buf.as_mut_ptr() as *mut libc::c_void, buf.len())
199 };
200 if size < 0 {
201 bail!("Failed to getxattr: {}", io::Error::last_os_error());
202 } else if size != SHA256_HASH_SIZE as isize {
203 bail!("Unexpected hash size: {}", size);
204 } else {
205 Ok(buf)
206 }
207}