Merge "A stopped VM with no files is deleted"
diff --git a/apex/Android.bp b/apex/Android.bp
index dce8edd..bdea039 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -3,15 +3,8 @@
}
microdroid_filesystem_images = [
- "microdroid_boot",
- "microdroid_bootconfig_debuggable",
- "microdroid_bootconfig_normal",
- "microdroid_init_boot",
"microdroid_super",
- "microdroid_uboot_env",
"microdroid_vbmeta",
- "microdroid_vbmeta_bootconfig",
- "microdroid_vendor_boot",
]
soong_config_module_type {
@@ -98,8 +91,6 @@
"microdroid_initrd_debuggable",
"microdroid_initrd_normal",
"microdroid.json",
- "microdroid_bootloader",
- "microdroid_bootloader.avbpubkey",
"microdroid_kernel",
],
host_required: [
@@ -146,7 +137,10 @@
},
},
required: [
+ // sign_virt_apex should be runnable from outside the source tree,
+ // therefore, any required tool should be listed in build/make/core/Makefile as well.
"img2simg",
+ "initrd_bootconfig",
"lpmake",
"lpunpack",
"simg2img",
@@ -167,6 +161,7 @@
// sign_virt_apex
"avbtool",
"img2simg",
+ "initrd_bootconfig",
"lpmake",
"lpunpack",
"sign_virt_apex",
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 557c8aa..3f3600d 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -24,7 +24,7 @@
sign_virt_apex uses external tools which are assumed to be available via PATH.
- avbtool (--avbtool can override the tool)
-- lpmake, lpunpack, simg2img, img2simg
+- lpmake, lpunpack, simg2img, img2simg, initrd_bootconfig
"""
import argparse
import hashlib
@@ -102,6 +102,11 @@
parser.add_argument(
'input_dir',
help='the directory having files to be packaged')
+ parser.add_argument(
+ '--do_not_update_bootconfigs',
+ action='store_true',
+ help='This will NOT update the vbmeta related bootconfigs while signing the apex.\
+ Used for testing only!!')
args = parser.parse_args(argv)
# preprocess --key_override into a map
args.key_overrides = {}
@@ -200,23 +205,19 @@
return info, descriptors
-# Look up a list of (key, value) with a key. Returns the value of the first matching pair.
+# Look up a list of (key, value) with a key. Returns the list of value(s) with the matching key.
+# The order of those values is maintained.
def LookUp(pairs, key):
- for k, v in pairs:
- if key == k:
- return v
- return None
+ return [v for (k, v) in pairs if k == key]
-def AddHashFooter(args, key, image_path):
+def AddHashFooter(args, key, image_path, partition_name, additional_descriptors=None):
if os.path.basename(image_path) in args.key_overrides:
key = args.key_overrides[os.path.basename(image_path)]
- info, descriptors = AvbInfo(args, image_path)
+ info, _ = AvbInfo(args, image_path)
if info:
- descriptor = LookUp(descriptors, 'Hash descriptor')
image_size = ReadBytesSize(info['Image size'])
algorithm = info['Algorithm']
- partition_name = descriptor['Partition Name']
partition_size = str(image_size)
cmd = ['avbtool', 'add_hash_footer',
@@ -227,6 +228,9 @@
'--image', image_path]
if args.signing_args:
cmd.extend(shlex.split(args.signing_args))
+ if additional_descriptors:
+ for image in additional_descriptors:
+ cmd.extend(['--include_descriptors_from_image', image])
RunCommand(args, cmd)
@@ -235,7 +239,7 @@
key = args.key_overrides[os.path.basename(image_path)]
info, descriptors = AvbInfo(args, image_path)
if info:
- descriptor = LookUp(descriptors, 'Hashtree descriptor')
+ descriptor = LookUp(descriptors, 'Hashtree descriptor')[0]
image_size = ReadBytesSize(info['Image size'])
algorithm = info['Algorithm']
partition_name = descriptor['Partition Name']
@@ -254,6 +258,82 @@
RunCommand(args, cmd)
+def UpdateVbmetaBootconfig(args, initrds, vbmeta_img):
+ # Update the bootconfigs in ramdisk
+ def detach_bootconfigs(initrd_bc, initrd, bc):
+ cmd = ['initrd_bootconfig', 'detach', initrd_bc, initrd, bc]
+ RunCommand(args, cmd)
+
+ def attach_bootconfigs(initrd_bc, initrd, bc):
+ cmd = ['initrd_bootconfig', 'attach',
+ initrd, bc, '--output', initrd_bc]
+ RunCommand(args, cmd)
+
+ # Validate that avb version used while signing the apex is the same as used by build server
+ def validate_avb_version(bootconfigs):
+ cmd = ['avbtool', 'version']
+ stdout, _ = RunCommand(args, cmd)
+ avb_version_curr = stdout.split(" ")[1].strip()
+ avb_version_curr = avb_version_curr[0:avb_version_curr.rfind('.')]
+
+ avb_version_bc = re.search(
+ r"androidboot.vbmeta.avb_version = \"([^\"]*)\"", bootconfigs).group(1)
+ if avb_version_curr != avb_version_bc:
+ raise Exception(f'AVB version mismatch between current & one & \
+ used to build bootconfigs:{avb_version_curr}&{avb_version_bc}')
+
+ def calc_vbmeta_digest():
+ cmd = ['avbtool', 'calculate_vbmeta_digest', '--image',
+ vbmeta_img, '--hash_algorithm', 'sha256']
+ stdout, _ = RunCommand(args, cmd)
+ return stdout.strip()
+
+ def calc_vbmeta_size():
+ cmd = ['avbtool', 'info_image', '--image', vbmeta_img]
+ stdout, _ = RunCommand(args, cmd)
+ size = 0
+ for line in stdout.split("\n"):
+ line = line.split(":")
+ if line[0] in ['Header Block', 'Authentication Block', 'Auxiliary Block']:
+ size += int(line[1].strip()[0:-6])
+ return size
+
+ def update_vbmeta_digest(bootconfigs):
+ # Update androidboot.vbmeta.digest in bootconfigs
+ result = re.search(
+ r"androidboot.vbmeta.digest = \"[^\"]*\"", bootconfigs)
+ if not result:
+ raise ValueError("Failed to find androidboot.vbmeta.digest")
+
+ return bootconfigs.replace(result.group(),
+ f'androidboot.vbmeta.digest = "{calc_vbmeta_digest()}"')
+
+ def update_vbmeta_size(bootconfigs):
+ # Update androidboot.vbmeta.size in bootconfigs
+ result = re.search(r"androidboot.vbmeta.size = [0-9]+", bootconfigs)
+ if not result:
+ raise ValueError("Failed to find androidboot.vbmeta.size")
+ return bootconfigs.replace(result.group(),
+ f'androidboot.vbmeta.size = {calc_vbmeta_size()}')
+
+ with tempfile.TemporaryDirectory() as work_dir:
+ tmp_initrd = os.path.join(work_dir, 'initrd')
+ tmp_bc = os.path.join(work_dir, 'bc')
+
+ for initrd in initrds:
+ detach_bootconfigs(initrd, tmp_initrd, tmp_bc)
+ bc_file = open(tmp_bc, "rt", encoding="utf-8")
+ bc_data = bc_file.read()
+ validate_avb_version(bc_data)
+ bc_data = update_vbmeta_digest(bc_data)
+ bc_data = update_vbmeta_size(bc_data)
+ bc_file.close()
+ bc_file = open(tmp_bc, "wt", encoding="utf-8")
+ bc_file.write(bc_data)
+ bc_file.flush()
+ attach_bootconfigs(initrd, tmp_initrd, tmp_bc)
+
+
def MakeVbmetaImage(args, key, vbmeta_img, images=None, chained_partitions=None):
if os.path.basename(vbmeta_img) in args.key_overrides:
key = args.key_overrides[os.path.basename(vbmeta_img)]
@@ -318,43 +398,21 @@
RunCommand(args, cmd)
-def ReplaceBootloaderPubkey(args, key, bootloader, bootloader_pubkey):
- if os.path.basename(bootloader) in args.key_overrides:
- key = args.key_overrides[os.path.basename(bootloader)]
- # read old pubkey before replacement
- with open(bootloader_pubkey, 'rb') as f:
- old_pubkey = f.read()
-
- # replace bootloader pubkey (overwrite the old one with the new one)
- ExtractAvbPubkey(args, key, bootloader_pubkey)
-
- # read new pubkey
- with open(bootloader_pubkey, 'rb') as f:
- new_pubkey = f.read()
-
- assert len(old_pubkey) == len(new_pubkey)
-
- # replace pubkey embedded in bootloader
- with open(bootloader, 'r+b') as bl_f:
- pos = bl_f.read().find(old_pubkey)
- assert pos != -1
- bl_f.seek(pos)
- bl_f.write(new_pubkey)
-
+def GenVbmetaImage(args, image, output, partition_name):
+ cmd = ['avbtool', 'add_hash_footer', '--dynamic_partition_size',
+ '--do_not_append_vbmeta_image',
+ '--partition_name', partition_name,
+ '--image', image,
+ '--output_vbmeta_image', output]
+ RunCommand(args, cmd)
# dict of (key, file) for re-sign/verification. keys are un-versioned for readability.
virt_apex_files = {
- 'bootloader.pubkey': 'etc/microdroid_bootloader.avbpubkey',
- 'bootloader': 'etc/fs/microdroid_bootloader',
- 'boot.img': 'etc/fs/microdroid_boot.img',
- 'vendor_boot.img': 'etc/fs/microdroid_vendor_boot.img',
- 'init_boot.img': 'etc/fs/microdroid_init_boot.img',
- 'super.img': 'etc/fs/microdroid_super.img',
+ 'kernel': 'etc/fs/microdroid_kernel',
'vbmeta.img': 'etc/fs/microdroid_vbmeta.img',
- 'vbmeta_bootconfig.img': 'etc/fs/microdroid_vbmeta_bootconfig.img',
- 'bootconfig.normal': 'etc/fs/microdroid_bootconfig.normal',
- 'bootconfig.debuggable': 'etc/fs/microdroid_bootconfig.debuggable',
- 'uboot_env.img': 'etc/fs/uboot_env.img'
+ 'super.img': 'etc/fs/microdroid_super.img',
+ 'initrd_normal.img': 'etc/microdroid_initrd_normal.img',
+ 'initrd_debuggable.img': 'etc/microdroid_initrd_debuggable.img',
}
@@ -371,17 +429,6 @@
system_a_img = os.path.join(unpack_dir.name, 'system_a.img')
vendor_a_img = os.path.join(unpack_dir.name, 'vendor_a.img')
- # Key(pubkey) embedded in bootloader should match with the one used to make VBmeta below
- # while it's okay to use different keys for other image files.
- replace_f = Async(ReplaceBootloaderPubkey, args,
- key, files['bootloader'], files['bootloader.pubkey'])
-
- # re-sign bootloader, boot.img, vendor_boot.img, and init_boot.img
- Async(AddHashFooter, args, key, files['bootloader'], wait=[replace_f])
- Async(AddHashFooter, args, key, files['boot.img'])
- Async(AddHashFooter, args, key, files['vendor_boot.img'])
- Async(AddHashFooter, args, key, files['init_boot.img'])
-
# re-sign super.img
# 1. unpack super.img
# 2. resign system and vendor
@@ -390,27 +437,34 @@
system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
partitions = {"system_a": system_a_img, "vendor_a": vendor_a_img}
- Async(MakeSuperImage, args, partitions, files['super.img'], wait=[system_a_f, vendor_a_f])
+ Async(MakeSuperImage, args, partitions,
+ files['super.img'], wait=[system_a_f, vendor_a_f])
# re-generate vbmeta from re-signed {system_a, vendor_a}.img
- Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
- images=[system_a_img, vendor_a_img],
- wait=[system_a_f, vendor_a_f])
+ vbmeta_f = Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
+ images=[system_a_img, vendor_a_img],
+ wait=[system_a_f, vendor_a_f])
- # Re-sign bootconfigs and the uboot_env with the same key
- bootconfig_sign_key = key
- Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.normal'])
- Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.debuggable'])
- Async(AddHashFooter, args, bootconfig_sign_key, files['uboot_env.img'])
+ vbmeta_bc_f = None
+ if not args.do_not_update_bootconfigs:
+ vbmeta_bc_f = Async(UpdateVbmetaBootconfig, args,
+ [files['initrd_normal.img'],
+ files['initrd_debuggable.img']], files['vbmeta.img'],
+ wait=[vbmeta_f])
- # Re-sign vbmeta_bootconfig with chained_partitions to "bootconfig" and
- # "uboot_env". Note that, for now, `key` and `bootconfig_sign_key` are the
- # same, but technically they can be different. Vbmeta records pubkeys which
- # signed chained partitions.
- Async(MakeVbmetaImage, args, key, files['vbmeta_bootconfig.img'], chained_partitions={
- 'bootconfig': bootconfig_sign_key,
- 'uboot_env': bootconfig_sign_key,
- })
+ # Re-sign kernel. Note kernel's vbmeta contain addition descriptor from ramdisk(s)
+ initrd_normal_hashdesc = tempfile.NamedTemporaryFile(delete=False).name
+ initrd_debug_hashdesc = tempfile.NamedTemporaryFile(delete=False).name
+ initrd_n_f = Async(GenVbmetaImage, args, files['initrd_normal.img'],
+ initrd_normal_hashdesc, "initrd_normal",
+ wait=[vbmeta_bc_f] if vbmeta_bc_f is not None else [])
+ initrd_d_f = Async(GenVbmetaImage, args, files['initrd_debuggable.img'],
+ initrd_debug_hashdesc, "initrd_debug",
+ wait=[vbmeta_bc_f] if vbmeta_bc_f is not None else [])
+ Async(AddHashFooter, args, key, files['kernel'], partition_name="boot",
+ additional_descriptors=[
+ initrd_normal_hashdesc, initrd_debug_hashdesc],
+ wait=[initrd_n_f, initrd_d_f])
def VerifyVirtApex(args):
@@ -430,27 +484,16 @@
pubkey = f.read()
pubkey_digest = hashlib.sha1(pubkey).hexdigest()
- def contents(file):
- with open(file, 'rb') as f:
- return f.read()
-
- def check_equals_pubkey(file):
- assert contents(file) == pubkey, f'pubkey mismatch: {file}'
-
- def check_contains_pubkey(file):
- assert contents(file).find(pubkey) != -1, f'pubkey missing: {file}'
-
def check_avb_pubkey(file):
info, _ = AvbInfo(args, file)
assert info is not None, f'no avbinfo: {file}'
assert info['Public key (sha1)'] == pubkey_digest, f'pubkey mismatch: {file}'
for f in files.values():
- if f == files['bootloader.pubkey']:
- Async(check_equals_pubkey, f)
- elif f == files['bootloader']:
- Async(check_contains_pubkey, f)
- elif f == files['super.img']:
+ if f in (files['initrd_normal.img'], files['initrd_debuggable.img']):
+ # TODO(b/245277660): Verify that ramdisks contain the correct vbmeta digest
+ continue
+ if f == files['super.img']:
Async(check_avb_pubkey, system_a_img)
Async(check_avb_pubkey, vendor_a_img)
else:
@@ -467,7 +510,7 @@
SignVirtApex(args)
# ensure all tasks are completed without exceptions
AwaitAll(tasks)
- except: # pylint: disable=bare-except
+ except: # pylint: disable=bare-except
traceback.print_exc()
sys.exit(1)
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 529686d..9264692 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -240,37 +240,6 @@
],
}
-bootimg {
- name: "microdroid_boot",
- // We don't have kernel for arm and x86. But Soong demands one when it builds for
- // arm or x86 target. Satisfy that by providing an empty file as the kernel.
- kernel_prebuilt: "empty_kernel",
- arch: {
- arm64: {
- kernel_prebuilt: ":microdroid_kernel_prebuilts-6.1-arm64",
- },
- x86_64: {
- kernel_prebuilt: ":microdroid_kernel_prebuilts-6.1-x86_64",
- },
- },
-
- dtb_prebuilt: "dummy_dtb.img",
- header_version: "4",
- partition_name: "boot",
- use_avb: true,
- avb_private_key: ":microdroid_sign_key",
-}
-
-bootimg {
- name: "microdroid_init_boot",
- ramdisk_module: "microdroid_ramdisk",
- kernel_prebuilt: "empty_kernel",
- header_version: "4",
- partition_name: "init_boot",
- use_avb: true,
- avb_private_key: ":microdroid_sign_key",
-}
-
android_filesystem {
name: "microdroid_ramdisk",
deps: [
@@ -289,25 +258,6 @@
type: "compressed_cpio",
}
-bootimg {
- name: "microdroid_vendor_boot",
- ramdisk_module: "microdroid_vendor_ramdisk",
- dtb_prebuilt: "dummy_dtb.img",
- header_version: "4",
- vendor_boot: true,
- arch: {
- arm64: {
- bootconfig: ":microdroid_bootconfig_arm64_gen",
- },
- x86_64: {
- bootconfig: ":microdroid_bootconfig_x86_64_gen",
- },
- },
- partition_name: "vendor_boot",
- use_avb: true,
- avb_private_key: ":microdroid_sign_key",
-}
-
android_filesystem {
name: "microdroid_vendor_ramdisk",
deps: [
@@ -347,43 +297,6 @@
cmd: "cat $(in) > $(out)",
}
-vbmeta {
- name: "microdroid_vbmeta_bootconfig",
- partition_name: "vbmeta",
- private_key: ":microdroid_sign_key",
- chained_partitions: [
- {
- name: "bootconfig",
- private_key: ":microdroid_sign_key",
- },
- {
- name: "uboot_env",
- private_key: ":microdroid_sign_key",
- },
- ],
-}
-
-// python -c "import hashlib; print(hashlib.sha256(b'bootconfig').hexdigest())"
-bootconfig_salt = "e158851fbebb402e1f18ea9372ea2f76b4dea23eceb5c4b92e5b27ade8537f5b"
-
-avb_add_hash_footer {
- name: "microdroid_bootconfig_normal",
- src: "bootconfig.normal",
- filename: "microdroid_bootconfig.normal",
- partition_name: "bootconfig",
- private_key: ":microdroid_sign_key",
- salt: bootconfig_salt,
-}
-
-avb_add_hash_footer {
- name: "microdroid_bootconfig_debuggable",
- src: "bootconfig.debuggable",
- filename: "microdroid_bootconfig.debuggable",
- partition_name: "bootconfig",
- private_key: ":microdroid_sign_key",
- salt: bootconfig_salt,
-}
-
prebuilt_etc {
name: "microdroid_fstab",
src: "fstab.microdroid",
@@ -391,89 +304,9 @@
installable: false,
}
-prebuilt_etc {
- name: "microdroid_bootloader",
- src: ":microdroid_bootloader_signed",
- arch: {
- x86_64: {
- // For unknown reason, the signed bootloader doesn't work on x86_64. Until the problem
- // is fixed, let's use the unsigned bootloader for the architecture.
- // TODO(b/185115783): remove this
- src: ":microdroid_bootloader_pubkey_replaced",
- },
- },
- relative_install_path: "fs",
- filename: "microdroid_bootloader",
-}
-
// python -c "import hashlib; print(hashlib.sha256(b'bootloader').hexdigest())"
bootloader_salt = "3b4a12881d11f33cff968a24d7c53723a8232cde9a8d91e29fdbd6a95ae6adf0"
-avb_add_hash_footer {
- name: "microdroid_bootloader_signed",
- src: ":microdroid_bootloader_pubkey_replaced",
- filename: "microdroid_bootloader",
- partition_name: "bootloader",
- private_key: ":microdroid_sign_key",
- salt: bootloader_salt,
-}
-
-// Replace avbpubkey of prebuilt bootloader with the avbpubkey of the signing key
-genrule {
- name: "microdroid_bootloader_pubkey_replaced",
- tools: ["replace_bytes"],
- srcs: [
- ":microdroid_crosvm_bootloader", // input (bootloader)
- ":microdroid_crosvm_bootloader.avbpubkey", // old bytes (old pubkey)
- ":microdroid_bootloader_avbpubkey_gen", // new bytes (new pubkey)
- ],
- out: ["bootloader-pubkey-replaced"],
- // 1. Copy the input to the output (replace_bytes modifies the file in-place)
- // 2. Replace embedded pubkey with new one.
- cmd: "cp $(location :microdroid_crosvm_bootloader) $(out) && " +
- "$(location replace_bytes) $(out) " +
- "$(location :microdroid_crosvm_bootloader.avbpubkey) " +
- "$(location :microdroid_bootloader_avbpubkey_gen)",
-}
-
-// Apex keeps a copy of avbpubkey embedded in bootloader so that embedded avbpubkey can be replaced
-// while re-signing bootloader.
-prebuilt_etc {
- name: "microdroid_bootloader.avbpubkey",
- src: ":microdroid_bootloader_avbpubkey_gen",
-}
-
-// Generate avbpukey from the signing key
-genrule {
- name: "microdroid_bootloader_avbpubkey_gen",
- tools: ["avbtool"],
- srcs: [":microdroid_sign_key"],
- out: ["bootloader.pubkey"],
- cmd: "$(location avbtool) extract_public_key " +
- "--key $(location :microdroid_sign_key) " +
- "--output $(out)",
-}
-
-// python -c "import hashlib; print(hashlib.sha256(b'uboot_env').hexdigest())"
-uboot_env_salt = "cbf2d76827ece5ca8d176a40c94ac6355edcf6511b4b887364a8c0e05850df10"
-
-avb_add_hash_footer {
- name: "microdroid_uboot_env",
- src: ":microdroid_uboot_env_gen",
- filename: "uboot_env.img",
- partition_name: "uboot_env",
- private_key: ":microdroid_sign_key",
- salt: uboot_env_salt,
-}
-
-genrule {
- name: "microdroid_uboot_env_gen",
- tools: ["mkenvimage_slim"],
- srcs: ["uboot-env.txt"],
- out: ["output.img"],
- cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt)",
-}
-
// Note that keys can be different for filesystem images even though we're using the same key
// for microdroid. However, the key signing VBmeta should match with the pubkey embedded in
// bootloader.
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index 7a95ce6..ff6314b 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -68,7 +68,7 @@
":microdroid_bootconfig_debuggable_src",
] + bootconfigs_arm64,
out: ["microdroid_initrd_debuggable_arm64"],
- cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
}
genrule {
@@ -79,7 +79,7 @@
":microdroid_bootconfig_debuggable_src",
] + bootconfigs_x86_64,
out: ["microdroid_initrd_debuggable_x86_64"],
- cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
}
genrule {
@@ -90,7 +90,7 @@
":microdroid_bootconfig_normal_src",
] + bootconfigs_arm64,
out: ["microdroid_initrd_normal_arm64"],
- cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
}
genrule {
@@ -101,7 +101,7 @@
":microdroid_bootconfig_normal_src",
] + bootconfigs_x86_64,
out: ["microdroid_initrd_normal_x86_64"],
- cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
}
prebuilt_etc {
diff --git a/microdroid/initrd/src/main.rs b/microdroid/initrd/src/main.rs
index 8c88000..3b0a7d2 100644
--- a/microdroid/initrd/src/main.rs
+++ b/microdroid/initrd/src/main.rs
@@ -12,31 +12,90 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Append bootconfig to initrd image
-use anyhow::Result;
+//! Attach/Detach bootconfigs to initrd image
+use anyhow::{bail, Result};
use clap::Parser;
+use std::cmp::min;
use std::fs::File;
-use std::io::{Read, Write};
+use std::io::{Read, Seek, SeekFrom, Write};
+use std::mem::size_of;
use std::path::PathBuf;
const FOOTER_ALIGNMENT: usize = 4;
const ZEROS: [u8; 4] = [0u8; 4_usize];
+const BOOTCONFIG_MAGIC: &str = "#BOOTCONFIG\n";
+// Footer includes [size(le32)][checksum(le32)][#BOOTCONFIG\n] at the end of bootconfigs.
+const INITRD_FOOTER_LEN: usize = 2 * std::mem::size_of::<u32>() + BOOTCONFIG_MAGIC.len();
#[derive(Parser, Debug)]
-struct Args {
- /// Initrd (without bootconfig)
- initrd: PathBuf,
- /// Bootconfig
- bootconfigs: Vec<PathBuf>,
- /// Output
- #[clap(long = "output")]
- output: PathBuf,
+enum Opt {
+ /// Append bootconfig(s) to initrd image
+ Attach {
+ /// Initrd (without bootconfigs) <- Input
+ initrd: PathBuf,
+ /// Bootconfigs <- Input
+ bootconfigs: Vec<PathBuf>,
+ /// Initrd (with bootconfigs) <- Output
+ #[clap(long = "output")]
+ output: PathBuf,
+ },
+
+ /// Detach the initrd & bootconfigs - this is required for cases when we update
+ /// bootconfigs in sign_virt_apex
+ Detach {
+ /// Initrd (with bootconfigs) <- Input
+ initrd_with_bootconfigs: PathBuf,
+ /// Initrd (without bootconfigs) <- Output
+ initrd: PathBuf,
+ /// Bootconfigs <- Output
+ bootconfigs: PathBuf,
+ },
}
fn get_checksum(file_path: &PathBuf) -> Result<u32> {
File::open(file_path)?.bytes().map(|x| Ok(x? as u32)).sum()
}
+// Copy n bytes of file_in to file_out. Note: copying starts from the current cursors of files.
+// On successful return, the files' cursors would have moved forward by k bytes.
+fn copyfile2file(file_in: &mut File, file_out: &mut File, n: usize) -> Result<()> {
+ let mut buf = vec![0; 1024];
+ let mut copied: usize = 0;
+ while copied < n {
+ let k = min(n - copied, buf.len());
+ file_in.read_exact(&mut buf[..k])?;
+ file_out.write_all(&buf[..k])?;
+ copied += k;
+ }
+ Ok(())
+}
+
+// Note: attaching & then detaching bootconfigs can lead to extra padding in bootconfigs
+fn detach_bootconfig(initrd_bc: PathBuf, initrd: PathBuf, bootconfig: PathBuf) -> Result<()> {
+ let mut initrd_bc = File::open(&initrd_bc)?;
+ let mut bootconfig = File::create(&bootconfig)?;
+ let mut initrd = File::create(&initrd)?;
+ let initrd_bc_size: usize = initrd_bc.metadata()?.len().try_into()?;
+
+ initrd_bc.seek(SeekFrom::End(-(BOOTCONFIG_MAGIC.len() as i64)))?;
+ let mut magic_buf = [0; BOOTCONFIG_MAGIC.len()];
+ initrd_bc.read_exact(&mut magic_buf)?;
+ if magic_buf != BOOTCONFIG_MAGIC.as_bytes() {
+ bail!("BOOTCONFIG_MAGIC not found in initrd. Bootconfigs might not be attached correctly");
+ }
+ let mut size_buf = [0; size_of::<u32>()];
+ initrd_bc.seek(SeekFrom::End(-(INITRD_FOOTER_LEN as i64)))?;
+ initrd_bc.read_exact(&mut size_buf)?;
+ let bc_size: usize = u32::from_le_bytes(size_buf) as usize;
+
+ let initrd_size: usize = initrd_bc_size - bc_size - INITRD_FOOTER_LEN;
+
+ initrd_bc.seek(SeekFrom::Start(0))?;
+ copyfile2file(&mut initrd_bc, &mut initrd, initrd_size)?;
+ copyfile2file(&mut initrd_bc, &mut bootconfig, bc_size)?;
+ Ok(())
+}
+
// Bootconfig is attached to the initrd in the following way:
// [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
fn attach_bootconfig(initrd: PathBuf, bootconfigs: Vec<PathBuf>, output: PathBuf) -> Result<()> {
@@ -59,14 +118,21 @@
output_file.write_all(&ZEROS[..padding_size])?;
output_file.write_all(&((padding_size + bootconfig_size) as u32).to_le_bytes())?;
output_file.write_all(&checksum.to_le_bytes())?;
- output_file.write_all(b"#BOOTCONFIG\n")?;
+ output_file.write_all(BOOTCONFIG_MAGIC.as_bytes())?;
output_file.flush()?;
Ok(())
}
fn try_main() -> Result<()> {
- let args = Args::parse();
- attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
+ let args = Opt::parse();
+ match args {
+ Opt::Attach { initrd, bootconfigs, output } => {
+ attach_bootconfig(initrd, bootconfigs, output)?
+ }
+ Opt::Detach { initrd_with_bootconfigs, initrd, bootconfigs } => {
+ detach_bootconfig(initrd_with_bootconfigs, initrd, bootconfigs)?
+ }
+ };
Ok(())
}
@@ -82,6 +148,6 @@
#[test]
fn verify_args() {
// Check that the command parsing has been configured in a valid way.
- Args::command().debug_assert();
+ Opt::command().debug_assert();
}
}
diff --git a/pvmfw/avb/tests/api_test.rs b/pvmfw/avb/tests/api_test.rs
index 52119f3..b5300f9 100644
--- a/pvmfw/avb/tests/api_test.rs
+++ b/pvmfw/avb/tests/api_test.rs
@@ -175,8 +175,10 @@
.copy_from_slice(&wrong_offset.to_be_bytes());
// Assert.
- let _footer = extract_avb_footer(&kernel)?;
- //assert_eq!(wrong_offset, footer.vbmeta_offset);
+ let footer = extract_avb_footer(&kernel)?;
+ // footer is unaligned; copy vbmeta_offset to local variable
+ let vbmeta_offset = footer.vbmeta_offset;
+ assert_eq!(wrong_offset, vbmeta_offset);
assert_payload_verification_with_initrd_eq(
&kernel,
&load_latest_initrd_normal()?,
@@ -268,10 +270,10 @@
let mut kernel = load_latest_signed_kernel()?;
let footer = extract_avb_footer(&kernel)?;
let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
- //assert_eq!(
- // 0, vbmeta_header.flags,
- // "The disable flag should not be set in the latest kernel."
- //);
+
+ // vbmeta_header is unaligned; copy flags to local variable
+ let vbmeta_header_flags = vbmeta_header.flags;
+ assert_eq!(0, vbmeta_header_flags, "The disable flag should not be set in the latest kernel.");
let flags_addr = ptr::addr_of!(vbmeta_header.flags) as *const u8;
// SAFETY: It is safe as both raw pointers `flags_addr` and `vbmeta_header` are not null.
let flags_offset = unsafe { flags_addr.offset_from(ptr::addr_of!(vbmeta_header) as *const u8) };
@@ -282,11 +284,13 @@
.copy_from_slice(&AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.to_be_bytes());
// Assert.
- let _vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
- //assert_eq!(
- // AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED, vbmeta_header.flags,
- // "VBMeta verification flag should be disabled now."
- //);
+ let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
+ // vbmeta_header is unaligned; copy flags to local variable
+ let vbmeta_header_flags = vbmeta_header.flags;
+ assert_eq!(
+ AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED, vbmeta_header_flags,
+ "VBMeta verification flag should be disabled now."
+ );
assert_payload_verification_with_initrd_eq(
&kernel,
&load_latest_initrd_normal()?,
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 7679c57..6e0cf5a 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -29,6 +29,7 @@
// For re-sign test
"avbtool",
"img2simg",
+ "initrd_bootconfig",
"lpmake",
"lpunpack",
"mk_payload",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 87129bb..112041b 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -55,7 +55,6 @@
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -173,7 +172,11 @@
.isSuccess();
}
- private void resignVirtApex(File virtApexDir, File signingKey, Map<String, File> keyOverrides) {
+ private void resignVirtApex(
+ File virtApexDir,
+ File signingKey,
+ Map<String, File> keyOverrides,
+ boolean updateBootconfigs) {
File signVirtApex = findTestFile("sign_virt_apex");
RunUtil runUtil = new RunUtil();
@@ -184,6 +187,9 @@
List<String> command = new ArrayList<>();
command.add(signVirtApex.getAbsolutePath());
+ if (!updateBootconfigs) {
+ command.add("--do_not_update_bootconfigs");
+ }
keyOverrides.forEach(
(filename, keyFile) ->
command.add("--key_override " + filename + "=" + keyFile.getPath()));
@@ -271,7 +277,11 @@
}
private VmInfo runMicrodroidWithResignedImages(
- File key, Map<String, File> keyOverrides, boolean isProtected) throws Exception {
+ File key,
+ Map<String, File> keyOverrides,
+ boolean isProtected,
+ boolean updateBootconfigs)
+ throws Exception {
CommandRunner android = new CommandRunner(getDevice());
File virtApexDir = FileUtil.createTempDir("virt_apex");
@@ -284,7 +294,7 @@
assertWithMessage("Failed to pull " + VIRT_APEX + "etc")
.that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir)).isTrue();
- resignVirtApex(virtApexDir, key, keyOverrides);
+ resignVirtApex(virtApexDir, key, keyOverrides, updateBootconfigs);
// Push back re-signed virt APEX contents and updated microdroid.json
getDevice().pushDir(virtApexDir, TEST_ROOT);
@@ -460,7 +470,8 @@
// Act
VmInfo vmInfo =
- runMicrodroidWithResignedImages(key, /*keyOverrides=*/ Map.of(), protectedVm);
+ runMicrodroidWithResignedImages(
+ key, /*keyOverrides=*/ Map.of(), protectedVm, /*updateBootconfigs=*/ true);
// Assert
vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
@@ -471,16 +482,15 @@
vmInfo.mProcess.destroy();
}
- // TODO(b/245277660): Resigning the system/vendor image changes the vbmeta hash.
- // So, unless vbmeta related bootconfigs are updated the following test will fail
@Test
- @Ignore("b/245277660")
@CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
throws Exception {
File key = findTestFile("test.com.android.virt.pem");
Map<String, File> keyOverrides = Map.of();
- VmInfo vmInfo = runMicrodroidWithResignedImages(key, keyOverrides, /*isProtected=*/ false);
+ VmInfo vmInfo =
+ runMicrodroidWithResignedImages(
+ key, keyOverrides, /*isProtected=*/ false, /*updateBootconfigs=*/ true);
// Device online means that boot must have succeeded.
adbConnectToMicrodroid(getDevice(), vmInfo.mCid);
vmInfo.mProcess.destroy();
@@ -491,10 +501,10 @@
public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig() throws Exception {
// Sign everything with key1 except vbmeta
File key = findTestFile("test.com.android.virt.pem");
- File key2 = findTestFile("test2.com.android.virt.pem");
- Map<String, File> keyOverrides = Map.of("microdroid_vbmeta.img", key2);
// To be able to stop it, it should be a daemon.
- VmInfo vmInfo = runMicrodroidWithResignedImages(key, keyOverrides, /*isProtected=*/ false);
+ VmInfo vmInfo =
+ runMicrodroidWithResignedImages(
+ key, Map.of(), /*isProtected=*/ false, /*updateBootconfigs=*/ false);
// Wait so that init can print errors to console (time in cuttlefish >> in real device)
assertThatEventually(
100000,
@@ -839,6 +849,64 @@
assertThat(ret).contains("Payload binary name must not specify a path");
}
+ @Test
+ @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
+ public void testAllVbmetaUseSHA256() throws Exception {
+ File virtApexDir = FileUtil.createTempDir("virt_apex");
+ // Pull the virt apex's etc/ directory (which contains images)
+ File virtApexEtcDir = new File(virtApexDir, "etc");
+ // We need only etc/ directory for images
+ assertWithMessage("Failed to mkdir " + virtApexEtcDir)
+ .that(virtApexEtcDir.mkdirs())
+ .isTrue();
+ assertWithMessage("Failed to pull " + VIRT_APEX + "etc")
+ .that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir))
+ .isTrue();
+
+ checkHashAlgorithm(virtApexEtcDir);
+ }
+
+ private String avbInfo(String image_path) throws Exception {
+ File avbtool = findTestFile("avbtool");
+ List<String> command =
+ Arrays.asList(avbtool.getAbsolutePath(), "info_image", "--image", image_path);
+ CommandResult result =
+ new RunUtil().runTimedCmd(5000, "/bin/bash", "-c", String.join(" ", command));
+ String out = result.getStdout();
+ String err = result.getStderr();
+ assertWithMessage(
+ "Command "
+ + command
+ + " failed."
+ + ":\n\tout: "
+ + out
+ + "\n\terr: "
+ + err
+ + "\n")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
+ return out;
+ }
+
+ private void checkHashAlgorithm(File virtApexEtcDir) throws Exception {
+ List<String> images =
+ Arrays.asList(
+ // kernel image (contains descriptors from initrd(s) as well)
+ "/fs/microdroid_kernel",
+ // vbmeta partition (contains descriptors from vendor/system images)
+ "/fs/microdroid_vbmeta.img");
+
+ for (String path : images) {
+ String info = avbInfo(virtApexEtcDir + path);
+ Pattern pattern = Pattern.compile("Hash Algorithm:[ ]*(sha1|sha256)");
+ Matcher m = pattern.matcher(info);
+ while (m.find()) {
+ assertThat(m.group(1)).isEqualTo("sha256");
+ }
+ }
+ }
+
@Before
public void setUp() throws Exception {
testIfDeviceIsCapable(getDevice());
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 4773c0b..8f88daf 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -78,9 +78,9 @@
/// triggered.
static ref BOOT_HANGUP_TIMEOUT: Duration = if nested_virt::is_nested_virtualization().unwrap() {
// Nested virtualization is slow, so we need a longer timeout.
- Duration::from_secs(100)
+ Duration::from_secs(300)
} else {
- Duration::from_secs(10)
+ Duration::from_secs(30)
};
}