Add way to disable dm-verity allowing rootfs to be writable.
This feature already exist in Android's current verified boot
implementation and can be enabled by running 'adb disable-verity'. As
it's very useful for developers (it allows replacing e.g. binaries on
the root filesystem) we want AVB to have this feature as well.
First, add a 'flags' field in the VBMeta struct with a single possible
flag value HASHTREE_DISABLED (we can add more flags in the future).
Second, to enable the feature we essentially need to pass
root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)
instead of
dm="1 vroot ... PARTUUID=$(ANDROID_SYSTEM_PARTUUID) ... " root=0xfd00
To do this cleanly and keep all the details about dm-verity setup
outside the bootloader binary, introduce a flags field to the
command-line descriptor allowing the bootloader to skip the
command-line snippet depending on whether HASHTREE_DISABLED is set or
not. With this in place, modify avbtool to generate two kernel
command-line descriptors - one if HASHTREE_DISABLED is set and one if
it's not.
One note is that the VBMeta flag HASHTREE_DISABLED will never be used
at image build time. Instead, it's expected that 'adb disable-verity'
will set the flag by writing to vbmeta_a or vbmeta_b directly. This
will of course cause the image to not be verified but if the device is
unlocked the bootloader will boot it anyway .. this is because of the
previous CL with subject "Enable operations on unlocked devices."
I tried all this using my toy UEFI-based bootloader using libavb and
here's the result. First the bootloader output when processing a
freshly built image (with lots of thing deleted for brevity):
ab_result=OK,
slot_suffix=_a,
command-line='dm="1 vroot none ro 1,0 [...]" root=0xfd00
androidboot.slot_suffix=_a
androidboot.vbmeta.device_state=unlocked [...]'
and once we've get a shell remounting rootfs rw fails:
$ su
# mount -orw,remount /
'/dev/root' is read-only
It's possible however to set the new HASHTREE_DISABLED flag by writing
to vbmeta_a:
# echo -n -e \\x01 | dd bs=1 oseek=123 count=1 \
of=/dev/block/pci/pci0000\:00/0000\:00\:01.1/by-name/vbmeta_a
1+0 records in
1+0 records out
1 bytes transferred in 0.001 secs (1000 bytes/sec)
When rebooting the bootloader now outputs the following:
ab_result=OK_WITH_VERIFICATION_ERROR,
slot_suffix=_a,
command-line='root=PARTUUID=c2531a08-1ff2-4c3e-9d9d-a50e5abd02c8
androidboot.slot_suffix=_a
androidboot.vbmeta.device_state=unlocked [...]'
and it's now possible to remount the root filesystem and write to it:
$ su
# mount -orw,remount /
# echo foo > /bar
# cat /bar
foo
with changes persisting across reboots.
Needless to say, disabling hashtree verification like this will ONLY
work if the device is unlocked. This is because the HASHTREE_DISABLED
flag is in the verified data.
Test: New unit tests and unit tests pass.
Test: Manually tested on UEFI based bootloader, see above.
Bug: 32949911
Change-Id: I9474ddd5f442be369cb0a551f03ac181cc41a265
diff --git a/test/avb_util_unittest.cc b/test/avb_util_unittest.cc
index ddc966a..d0d6b87 100644
--- a/test/avb_util_unittest.cc
+++ b/test/avb_util_unittest.cc
@@ -107,27 +107,36 @@
AvbKernelCmdlineDescriptor s;
AvbKernelCmdlineDescriptor bad;
uint64_t nbf;
+ uint32_t n32;
- // Specify 44 bytes of data past the end of the descriptor struct.
- nbf = 44 + sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor);
+ // Specify 40 bytes of data past the end of the descriptor struct.
+ nbf = 40 + sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor);
h.parent_descriptor.num_bytes_following = htobe64(nbf);
h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE);
- h.kernel_cmdline_length = htobe32(44);
+ h.kernel_cmdline_length = htobe32(40);
+
+ n32 = 0x11223344;
+ h.flags = htobe32(n32);
+ n32++;
EXPECT_NE(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&h, &s));
+ n32 = 0x11223344;
+ EXPECT_EQ(n32, s.flags);
+ n32++;
+
EXPECT_EQ(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE, s.parent_descriptor.tag);
EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
- EXPECT_EQ(44UL, s.kernel_cmdline_length);
+ EXPECT_EQ(40UL, s.kernel_cmdline_length);
// Check for bad tag.
bad = h;
bad.parent_descriptor.tag = htobe64(0xf00dd00d);
EXPECT_EQ(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&bad, &s));
- // Doesn't fit in 45 bytes.
+ // Doesn't fit in 41 bytes.
bad = h;
- bad.kernel_cmdline_length = htobe32(45);
+ bad.kernel_cmdline_length = htobe32(41);
EXPECT_EQ(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&bad, &s));
}