Filter-out android.vbmeta.device when reading bootconfig
android.vbmeta.device contains UUID of the vbmeta partition. The UUID
may change everytime the VM is started because the UUID is recorded in
the composite disk image which exists only while the VM is running. When
the VM is stopped, the disk image is deleted (to save disk space) and
re-created at the next time the VM is started.
So far, even if a single bit is changed, we have refused to boot the VM.
This is too aggressive given that the UUID can change every time. To
address this issue, filter-out android.vbmeta.device config when reading
bootconfig. This doesn't loosen the security because we still require
that other configs (digest, debug mode, etc.) to be the same.
Bug: 208442532
Test: run a VM multiple times with the same debug level -> boots
run a VM multiple times with different debug levels -> not boots (as
expected)
Change-Id: I5af4bcdc1a18fcbc25e152b8e4af0dc8e9d8dc31
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index fccf031..4420a49 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -34,6 +34,7 @@
use rustutils::system_properties;
use rustutils::system_properties::PropertyWatcher;
use std::fs::{self, create_dir, File, OpenOptions};
+use std::io::BufRead;
use std::os::unix::io::{FromRawFd, IntoRawFd};
use std::path::Path;
use std::process::{Child, Command, Stdio};
@@ -435,7 +436,31 @@
fn get_bootconfig() -> Result<&'static Vec<u8>> {
static VAL: OnceCell<Vec<u8>> = OnceCell::new();
- VAL.get_or_try_init(|| fs::read("/proc/bootconfig").context("Failed to read bootconfig"))
+ VAL.get_or_try_init(|| -> Result<Vec<u8>> {
+ let f = File::open("/proc/bootconfig")?;
+
+ // Filter-out androidboot.vbmeta.device which contains UUID of the vbmeta partition. That
+ // UUID could change everytime when the same VM is started because the composite disk image
+ // is ephemeral. A change in UUID is okay as long as other configs (e.g.
+ // androidboot.vbmeta.digest) remain same.
+ Ok(std::io::BufReader::new(f)
+ .lines()
+ // note: this try_fold is to early return when we fail to read a line from the file
+ .try_fold(Vec::new(), |mut lines, line| {
+ line.map(|s| {
+ lines.push(s);
+ lines
+ })
+ })?
+ .into_iter()
+ .filter(|line| {
+ let tokens: Vec<&str> = line.splitn(2, '=').collect();
+ // note: if `line` doesn't have =, tokens[0] is the entire line.
+ tokens[0].trim() != "androidboot.vbmeta.device"
+ })
+ .flat_map(|line| (line + "\n").into_bytes())
+ .collect())
+ })
}
fn load_config(path: &Path) -> Result<VmPayloadConfig> {